diff options
| author | sander <[email protected]> | 2023-04-11 13:48:34 +0200 |
|---|---|---|
| committer | sander <[email protected]> | 2023-04-11 13:48:34 +0200 |
| commit | c309797488a4f33dfab659fdb2908eff76e16e40 (patch) | |
| tree | 2ee72042d9f06bf694fea8dd4cd2ba3e2e2cb10b | |
| parent | 6b2aaacf830d69fcb05f9611d3780f56b4ae82bc (diff) | |
| parent | b150b9506b2f0502065dc1b22eccc6448f611bff (diff) | |
merge embassy/master
160 files changed, 6694 insertions, 3495 deletions
| @@ -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" | |||
| 6 | license = "MIT OR Apache-2.0" | 6 | license = "MIT OR Apache-2.0" |
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } |
| 11 | embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } | 11 | embassy-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" | |||
| 8 | cortex-m = "0.7" | 8 | cortex-m = "0.7" |
| 9 | cortex-m-rt = "0.7" | 9 | cortex-m-rt = "0.7" |
| 10 | embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } | 10 | embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } |
| 11 | embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] } | 11 | embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3.0" | 13 | defmt = "0.3.0" |
| 14 | defmt-rtt = "0.3.0" | 14 | defmt-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] |
| 26 | defmt = { version = "0.3", optional = true } | 26 | defmt = { version = "0.3", optional = true } |
| 27 | digest = "0.10" | ||
| 27 | log = { version = "0.4", optional = true } | 28 | log = { version = "0.4", optional = true } |
| 28 | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } | 29 | ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } |
| 29 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } | 30 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } |
| @@ -37,6 +38,7 @@ log = "0.4" | |||
| 37 | env_logger = "0.9" | 38 | env_logger = "0.9" |
| 38 | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version | 39 | rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version |
| 39 | futures = { version = "0.3", features = ["executor"] } | 40 | futures = { version = "0.3", features = ["executor"] } |
| 41 | sha1 = "0.10.5" | ||
| 40 | 42 | ||
| 41 | [dev-dependencies.ed25519-dalek] | 43 | [dev-dependencies.ed25519-dalek] |
| 42 | default_features = false | 44 | default_features = false |
| @@ -50,4 +52,4 @@ ed25519-salty = ["dep:salty", "_verify"] | |||
| 50 | nightly = ["dep:embedded-storage-async"] | 52 | nightly = ["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 @@ | |||
| 1 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | ||
| 2 | |||
| 3 | use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; | ||
| 4 | |||
| 5 | /// Errors returned by bootloader | ||
| 6 | #[derive(PartialEq, Eq, Debug)] | ||
| 7 | pub enum BootError { | ||
| 8 | /// Error from flash. | ||
| 9 | Flash(NorFlashErrorKind), | ||
| 10 | /// Invalid bootloader magic | ||
| 11 | BadMagic, | ||
| 12 | } | ||
| 13 | |||
| 14 | #[cfg(feature = "defmt")] | ||
| 15 | impl 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 | |||
| 24 | impl<E> From<E> for BootError | ||
| 25 | where | ||
| 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. | ||
| 34 | pub 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 | |||
| 52 | trait FlashConfigEx { | ||
| 53 | fn page_size() -> u32; | ||
| 54 | } | ||
| 55 | |||
| 56 | impl<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. | ||
| 64 | pub 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 | |||
| 78 | impl 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 | |||
| 376 | fn 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. | ||
| 384 | pub struct BootFlash<F> | ||
| 385 | where | ||
| 386 | F: NorFlash, | ||
| 387 | { | ||
| 388 | flash: F, | ||
| 389 | } | ||
| 390 | |||
| 391 | impl<F> BootFlash<F> | ||
| 392 | where | ||
| 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 | |||
| 401 | impl<F> ErrorType for BootFlash<F> | ||
| 402 | where | ||
| 403 | F: NorFlash, | ||
| 404 | { | ||
| 405 | type Error = F::Error; | ||
| 406 | } | ||
| 407 | |||
| 408 | impl<F> NorFlash for BootFlash<F> | ||
| 409 | where | ||
| 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 | |||
| 424 | impl<F> ReadNorFlash for BootFlash<F> | ||
| 425 | where | ||
| 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. | ||
| 440 | pub struct SingleFlashConfig<'a, F> | ||
| 441 | where | ||
| 442 | F: NorFlash, | ||
| 443 | { | ||
| 444 | flash: &'a mut F, | ||
| 445 | } | ||
| 446 | |||
| 447 | impl<'a, F> SingleFlashConfig<'a, F> | ||
| 448 | where | ||
| 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 | |||
| 457 | impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> | ||
| 458 | where | ||
| 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. | ||
| 477 | pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 478 | where | ||
| 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 | |||
| 488 | impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 489 | where | ||
| 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 | |||
| 500 | impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 501 | where | ||
| 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)] | ||
| 522 | mod 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 @@ | |||
| 1 | use digest::typenum::U64; | ||
| 2 | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | ||
| 3 | use ed25519_dalek::Digest as _; | ||
| 4 | |||
| 5 | pub struct Sha512(ed25519_dalek::Sha512); | ||
| 6 | |||
| 7 | impl Default for Sha512 { | ||
| 8 | fn default() -> Self { | ||
| 9 | Self(ed25519_dalek::Sha512::new()) | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | impl Update for Sha512 { | ||
| 14 | fn update(&mut self, data: &[u8]) { | ||
| 15 | self.0.update(data) | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | impl 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 | |||
| 26 | impl OutputSizeUser for Sha512 { | ||
| 27 | type OutputSize = U64; | ||
| 28 | } | ||
| 29 | |||
| 30 | impl 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")] | ||
| 2 | pub(crate) mod ed25519_dalek; | ||
| 3 | |||
| 4 | #[cfg(feature = "ed25519-salty")] | ||
| 5 | pub(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 @@ | |||
| 1 | use digest::typenum::U64; | ||
| 2 | use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; | ||
| 3 | |||
| 4 | pub struct Sha512(salty::Sha512); | ||
| 5 | |||
| 6 | impl Default for Sha512 { | ||
| 7 | fn default() -> Self { | ||
| 8 | Self(salty::Sha512::new()) | ||
| 9 | } | ||
| 10 | } | ||
| 11 | |||
| 12 | impl Update for Sha512 { | ||
| 13 | fn update(&mut self, data: &[u8]) { | ||
| 14 | self.0.update(data) | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | impl 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 | |||
| 25 | impl OutputSizeUser for Sha512 { | ||
| 26 | type OutputSize = U64; | ||
| 27 | } | ||
| 28 | |||
| 29 | impl 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 @@ | |||
| 1 | use digest::Digest; | ||
| 2 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | ||
| 3 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | ||
| 4 | |||
| 5 | use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; | ||
| 6 | |||
| 7 | /// Errors returned by FirmwareUpdater | ||
| 8 | #[derive(Debug)] | ||
| 9 | pub enum FirmwareUpdaterError { | ||
| 10 | /// Error from flash. | ||
| 11 | Flash(NorFlashErrorKind), | ||
| 12 | /// Signature errors. | ||
| 13 | Signature(signature::Error), | ||
| 14 | } | ||
| 15 | |||
| 16 | #[cfg(feature = "defmt")] | ||
| 17 | impl 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 | |||
| 26 | impl<E> From<E> for FirmwareUpdaterError | ||
| 27 | where | ||
| 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 | ||
| 37 | pub struct FirmwareUpdater { | ||
| 38 | state: Partition, | ||
| 39 | dfu: Partition, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl 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 | |||
| 70 | impl 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)] | ||
| 508 | mod 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")] |
| 6 | mod fmt; | 6 | mod fmt; |
| 7 | 7 | ||
| 8 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | 8 | mod boot_loader; |
| 9 | mod digest_adapters; | ||
| 10 | mod firmware_updater; | ||
| 11 | mod mem_flash; | ||
| 12 | mod partition; | ||
| 9 | 13 | ||
| 10 | #[cfg(feature = "nightly")] | 14 | pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; |
| 11 | use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; | 15 | pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; |
| 16 | pub use partition::Partition; | ||
| 12 | 17 | ||
| 13 | const BOOT_MAGIC: u8 = 0xD0; | 18 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 14 | const SWAP_MAGIC: u8 = 0xF0; | 19 | pub(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))] | ||
| 19 | pub struct Partition { | ||
| 20 | /// Start of the flash region. | ||
| 21 | pub from: usize, | ||
| 22 | /// End of the flash region. | ||
| 23 | pub to: usize, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl 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)] | ||
| 51 | pub enum BootError { | ||
| 52 | /// Error from flash. | ||
| 53 | Flash(NorFlashErrorKind), | ||
| 54 | /// Invalid bootloader magic | ||
| 55 | BadMagic, | ||
| 56 | } | ||
| 57 | |||
| 58 | #[cfg(feature = "defmt")] | ||
| 59 | impl 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 | |||
| 68 | impl<E> From<E> for BootError | ||
| 69 | where | ||
| 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))] |
| 79 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); | 33 | pub 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. | ||
| 94 | pub 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 | ||
| 103 | pub 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. | ||
| 121 | pub 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 | |||
| 133 | impl 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 | |||
| 421 | fn 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. | ||
| 429 | pub struct SingleFlashConfig<'a, F> | ||
| 430 | where | ||
| 431 | F: Flash, | ||
| 432 | { | ||
| 433 | flash: &'a mut F, | ||
| 434 | } | ||
| 435 | |||
| 436 | impl<'a, F> SingleFlashConfig<'a, F> | ||
| 437 | where | ||
| 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 | |||
| 446 | impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> | ||
| 447 | where | ||
| 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. | ||
| 466 | pub struct BootFlash<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> | ||
| 467 | where | ||
| 468 | F: NorFlash + ReadNorFlash, | ||
| 469 | { | ||
| 470 | flash: F, | ||
| 471 | } | ||
| 472 | |||
| 473 | impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<F, BLOCK_SIZE, ERASE_VALUE> | ||
| 474 | where | ||
| 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 | |||
| 483 | impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> | ||
| 484 | where | ||
| 485 | F: NorFlash + ReadNorFlash, | ||
| 486 | { | ||
| 487 | const BLOCK_SIZE: usize = BLOCK_SIZE; | ||
| 488 | const ERASE_VALUE: u8 = ERASE_VALUE; | ||
| 489 | } | ||
| 490 | |||
| 491 | impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> | ||
| 492 | where | ||
| 493 | F: ReadNorFlash + NorFlash, | ||
| 494 | { | ||
| 495 | type Error = F::Error; | ||
| 496 | } | ||
| 497 | |||
| 498 | impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> | ||
| 499 | where | ||
| 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 | |||
| 514 | impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE> | ||
| 515 | where | ||
| 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. | ||
| 530 | pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 531 | where | ||
| 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 | |||
| 541 | impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 542 | where | ||
| 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 | |||
| 553 | impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> | ||
| 554 | where | ||
| 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)] | ||
| 575 | pub enum FirmwareUpdaterError { | ||
| 576 | /// Error from flash. | ||
| 577 | Flash(NorFlashErrorKind), | ||
| 578 | /// Signature errors. | ||
| 579 | Signature(signature::Error), | ||
| 580 | } | ||
| 581 | |||
| 582 | #[cfg(feature = "defmt")] | ||
| 583 | impl 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 | |||
| 592 | impl<E> From<E> for FirmwareUpdaterError | ||
| 593 | where | ||
| 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 | ||
| 603 | pub struct FirmwareUpdater { | ||
| 604 | state: Partition, | ||
| 605 | dfu: Partition, | ||
| 606 | } | ||
| 607 | |||
| 608 | impl 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 | |||
| 636 | impl 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. | ||
| 1113 | pub struct FirmwareWriter(Partition); | ||
| 1114 | |||
| 1115 | impl 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)] |
| 1207 | mod tests { | 48 | mod 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 | |||
| 3 | use core::ops::{Bound, Range, RangeBounds}; | ||
| 4 | |||
| 5 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | ||
| 6 | use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; | ||
| 7 | |||
| 8 | pub 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)] | ||
| 14 | pub struct MemFlashError; | ||
| 15 | |||
| 16 | impl<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 | |||
| 54 | impl<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 | |||
| 62 | impl<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 | |||
| 68 | impl NorFlashError for MemFlashError { | ||
| 69 | fn kind(&self) -> NorFlashErrorKind { | ||
| 70 | NorFlashErrorKind::Other | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | impl<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 | |||
| 90 | impl<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 | |||
| 137 | impl<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 | |||
| 151 | impl<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 @@ | |||
| 1 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 2 | use 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))] | ||
| 7 | pub 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 | |||
| 14 | impl 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)] | ||
| 96 | mod 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; | |||
| 11 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 11 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 12 | 12 | ||
| 13 | /// A bootloader for nRF devices. | 13 | /// A bootloader for nRF devices. |
| 14 | pub struct BootLoader { | 14 | pub 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 | ||
| 20 | impl Default for BootLoader { | 19 | impl 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 | ||
| 59 | impl BootLoader { | 58 | impl<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 @@ | |||
| 5 | mod fmt; | 5 | mod fmt; |
| 6 | 6 | ||
| 7 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; | 7 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; |
| 8 | use embassy_rp::flash::{Flash, ERASE_SIZE, WRITE_SIZE}; | 8 | use embassy_rp::flash::{Flash, ERASE_SIZE}; |
| 9 | use embassy_rp::peripherals::{FLASH, WATCHDOG}; | 9 | use embassy_rp::peripherals::{FLASH, WATCHDOG}; |
| 10 | use embassy_rp::watchdog::Watchdog; | 10 | use embassy_rp::watchdog::Watchdog; |
| 11 | use embassy_time::Duration; | 11 | use embassy_time::Duration; |
| 12 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 12 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 13 | 13 | ||
| 14 | /// A bootloader for RP2040 devices. | 14 | /// A bootloader for RP2040 devices. |
| 15 | pub struct BootLoader { | 15 | pub 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 | ||
| 21 | impl BootLoader { | 20 | impl<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 | ||
| 57 | impl Default for BootLoader { | 55 | impl 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; | |||
| 7 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; | 7 | pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; |
| 8 | 8 | ||
| 9 | /// A bootloader for STM32 devices. | 9 | /// A bootloader for STM32 devices. |
| 10 | pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize> { | 10 | pub 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 | ||
| 16 | impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRITE_SIZE> { | 15 | impl<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 | ||
| 52 | impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> Default for BootLoader<PAGE_SIZE, WRITE_SIZE> { | 50 | impl<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 | |||
| 3 | use core::cell::UnsafeCell; | ||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | |||
| 6 | use atomic_polyfill::{AtomicBool, Ordering}; | ||
| 7 | use cortex_m::interrupt::InterruptNumber; | ||
| 8 | use cortex_m::peripheral::NVIC; | ||
| 9 | pub use embassy_executor::*; | ||
| 10 | |||
| 11 | #[derive(Clone, Copy)] | ||
| 12 | struct N(u16); | ||
| 13 | unsafe impl cortex_m::interrupt::InterruptNumber for N { | ||
| 14 | fn number(self) -> u16 { | ||
| 15 | self.0 | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | fn 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. | ||
| 44 | pub struct InterruptExecutor { | ||
| 45 | started: AtomicBool, | ||
| 46 | executor: UnsafeCell<MaybeUninit<raw::Executor>>, | ||
| 47 | } | ||
| 48 | |||
| 49 | unsafe impl Send for InterruptExecutor {} | ||
| 50 | unsafe impl Sync for InterruptExecutor {} | ||
| 51 | |||
| 52 | impl 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. |
| 6 | pub(crate) mod fmt; | 6 | pub(crate) mod fmt; |
| 7 | 7 | ||
| 8 | pub mod executor; | 8 | pub use embassy_executor as executor; |
| 9 | pub mod interrupt; | 9 | pub mod interrupt; |
| 10 | pub mod peripheral; | 10 | pub 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] |
| 20 | embassy-sync = { version = "0.1.0", path = "../embassy-sync" } | 20 | embassy-sync = { version = "0.1.0", path = "../embassy-sync" } |
| 21 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 21 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 23 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true } | 23 | embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } |
| 24 | embedded-storage = "0.3.0" | 24 | embedded-storage = "0.3.0" |
| 25 | embedded-storage-async = { version = "0.4.0", optional = true } | 25 | embedded-storage-async = { version = "0.4.0", optional = true } |
| 26 | nb = "1.0.0" | 26 | nb = "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 | //! ``` |
| 28 | use core::future::Future; | ||
| 29 | 28 | ||
| 30 | use embassy_sync::blocking_mutex::raw::RawMutex; | 29 | use embassy_sync::blocking_mutex::raw::RawMutex; |
| 31 | use embassy_sync::mutex::Mutex; | 30 | use embassy_sync::mutex::Mutex; |
| 32 | use embedded_hal_1::digital::OutputPin; | 31 | use embedded_hal_1::digital::OutputPin; |
| 33 | use embedded_hal_1::spi::ErrorType; | 32 | use embedded_hal_1::spi::Operation; |
| 34 | use embedded_hal_async::spi; | 33 | use embedded_hal_async::spi; |
| 35 | 34 | ||
| 36 | use crate::shared_bus::SpiDeviceError; | 35 | use 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 | ||
| 60 | unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS> | 59 | impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> |
| 61 | where | 60 | where |
| 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 | |||
| 87 | impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> | ||
| 88 | where | ||
| 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 | |||
| 115 | impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS> | ||
| 116 | where | ||
| 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 | ||
| 117 | unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> | 175 | impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> |
| 176 | where | ||
| 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 | |||
| 204 | impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> | ||
| 118 | where | 205 | where |
| 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> | 233 | impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> |
| 126 | where | 234 | where |
| 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 | ||
| 105 | impl<'a, M, BUS, E> embedded_hal_02::blocking::i2c::Write for I2cDevice<'_, M, BUS> | 77 | impl<'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; | |||
| 23 | use embassy_sync::blocking_mutex::raw::RawMutex; | 23 | use embassy_sync::blocking_mutex::raw::RawMutex; |
| 24 | use embassy_sync::blocking_mutex::Mutex; | 24 | use embassy_sync::blocking_mutex::Mutex; |
| 25 | use embedded_hal_1::digital::OutputPin; | 25 | use embedded_hal_1::digital::OutputPin; |
| 26 | use embedded_hal_1::spi; | 26 | use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite}; |
| 27 | use embedded_hal_1::spi::SpiBusFlush; | ||
| 28 | 27 | ||
| 29 | use crate::shared_bus::SpiDeviceError; | 28 | use crate::shared_bus::SpiDeviceError; |
| 30 | use crate::SetConfig; | 29 | use 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 | ||
| 53 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> | 52 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> |
| 54 | where | 53 | where |
| 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 | |||
| 78 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> | ||
| 79 | where | ||
| 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 | |||
| 104 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> | ||
| 105 | where | ||
| 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 | ||
| 149 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> | 203 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> |
| 150 | where | 204 | where |
| 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 | |||
| 229 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> | ||
| 230 | where | ||
| 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 | |||
| 255 | impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> | ||
| 256 | where | ||
| 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] |
| 15 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" | 15 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" |
| 16 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" | 16 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" |
| 17 | features = ["nightly", "defmt"] | 17 | features = ["nightly", "defmt", "pender-callback"] |
| 18 | flavors = [ | 18 | flavors = [ |
| 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] |
| 31 | features = ["std", "nightly", "defmt"] | 26 | default-target = "thumbv7em-none-eabi" |
| 27 | targets = ["thumbv7em-none-eabi"] | ||
| 28 | features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] | ||
| 32 | 29 | ||
| 33 | [features] | 30 | [features] |
| 34 | default = [] | 31 | |
| 35 | std = ["critical-section/std"] | 32 | # Architecture |
| 36 | wasm = ["dep:wasm-bindgen", "dep:js-sys"] | 33 | _arch = [] # some arch was picked |
| 34 | arch-std = ["_arch", "critical-section/std"] | ||
| 35 | arch-cortex-m = ["_arch", "dep:cortex-m"] | ||
| 36 | arch-xtensa = ["_arch"] | ||
| 37 | arch-riscv32 = ["_arch"] | ||
| 38 | arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] | ||
| 39 | |||
| 40 | # Enable creating a `Pender` from an arbitrary function pointer callback. | ||
| 41 | pender-callback = [] | ||
| 42 | |||
| 43 | # Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) | ||
| 44 | executor-thread = [] | ||
| 45 | # Enable the interrupt-mode executor (available in Cortex-M only) | ||
| 46 | executor-interrupt = [] | ||
| 37 | 47 | ||
| 38 | # Enable nightly-only features | 48 | # Enable nightly-only features |
| 39 | nightly = [] | 49 | nightly = [] |
| 40 | 50 | ||
| 51 | turbowakers = [] | ||
| 52 | |||
| 41 | integrated-timers = ["dep:embassy-time"] | 53 | integrated-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" } | |||
| 53 | embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} | 65 | embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} |
| 54 | atomic-polyfill = "1.0.1" | 66 | atomic-polyfill = "1.0.1" |
| 55 | critical-section = "1.1" | 67 | critical-section = "1.1" |
| 56 | cfg-if = "1.0.0" | ||
| 57 | static_cell = "1.0" | 68 | static_cell = "1.0" |
| 58 | 69 | ||
| 59 | # WASM dependencies | 70 | # arch-cortex-m dependencies |
| 71 | cortex-m = { version = "0.7.6", optional = true } | ||
| 72 | |||
| 73 | # arch-wasm dependencies | ||
| 60 | wasm-bindgen = { version = "0.2.82", optional = true } | 74 | wasm-bindgen = { version = "0.2.82", optional = true } |
| 61 | js-sys = { version = "0.3", optional = true } | 75 | js-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 @@ | |||
| 1 | use core::arch::asm; | 1 | #[cfg(feature = "executor-thread")] |
| 2 | use core::marker::PhantomData; | 2 | pub use thread::*; |
| 3 | use core::ptr; | 3 | #[cfg(feature = "executor-thread")] |
| 4 | 4 | mod thread { | |
| 5 | use 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 | |
| 17 | pub 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 | ||
| 22 | impl Executor { | 78 | #[cfg(feature = "executor-interrupt")] |
| 23 | /// Create a new Executor. | 79 | pub use interrupt::*; |
| 24 | pub fn new() -> Self { | 80 | #[cfg(feature = "executor-interrupt")] |
| 25 | Self { | 81 | mod 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 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | use core::ptr; | 2 | compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); |
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 4 | 3 | ||
| 5 | use super::{raw, Spawner}; | 4 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | ||
| 6 | #[cfg(feature = "executor-thread")] | ||
| 7 | mod 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}; |
| 9 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); | ||
| 10 | 13 | ||
| 11 | /// RISCV32 Executor | 14 | #[derive(Copy, Clone)] |
| 12 | pub struct Executor { | 15 | pub(crate) struct ThreadPender; |
| 13 | inner: raw::Executor, | ||
| 14 | not_send: PhantomData<*mut ()>, | ||
| 15 | } | ||
| 16 | 16 | ||
| 17 | impl 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 @@ | |||
| 1 | use std::marker::PhantomData; | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | use std::sync::{Condvar, Mutex}; | 2 | compile_error!("`executor-interrupt` is not supported with `arch-std`."); |
| 3 | 3 | ||
| 4 | use super::{raw, Spawner}; | 4 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | ||
| 6 | #[cfg(feature = "executor-thread")] | ||
| 7 | mod thread { | ||
| 8 | use std::marker::PhantomData; | ||
| 9 | use std::sync::{Condvar, Mutex}; | ||
| 5 | 10 | ||
| 6 | /// Single-threaded std-based executor. | 11 | #[cfg(feature = "nightly")] |
| 7 | pub 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 | ||
| 13 | impl 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 | ||
| 58 | struct 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 | ||
| 63 | impl 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 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); | ||
| 2 | 3 | ||
| 3 | use js_sys::Promise; | 4 | #[cfg(feature = "executor-thread")] |
| 4 | use wasm_bindgen::prelude::*; | 5 | pub use thread::*; |
| 6 | #[cfg(feature = "executor-thread")] | ||
| 7 | mod thread { | ||
| 5 | 8 | ||
| 6 | use super::raw::util::UninitCell; | 9 | use core::marker::PhantomData; |
| 7 | use super::raw::{self}; | ||
| 8 | use super::Spawner; | ||
| 9 | 10 | ||
| 10 | /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. | 11 | #[cfg(feature = "nightly")] |
| 11 | pub 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 | ||
| 17 | pub(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 | ||
| 22 | impl 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 | ||
| 31 | impl 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 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | use core::ptr; | 2 | compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); |
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 4 | 3 | ||
| 5 | use super::{raw, Spawner}; | 4 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | ||
| 6 | #[cfg(feature = "executor-thread")] | ||
| 7 | mod 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}; |
| 9 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); | ||
| 10 | 13 | ||
| 11 | /// Xtensa Executor | 14 | #[derive(Copy, Clone)] |
| 12 | pub struct Executor { | 15 | pub(crate) struct ThreadPender; |
| 13 | inner: raw::Executor, | ||
| 14 | not_send: PhantomData<*mut ()>, | ||
| 15 | } | ||
| 16 | 16 | ||
| 17 | impl 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")] |
| 11 | pub use embassy_macros::task; | 11 | pub use embassy_macros::task; |
| 12 | 12 | ||
| 13 | cfg_if::cfg_if! { | 13 | macro_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 | } |
| 25 | check_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")] | ||
| 33 | mod arch; | ||
| 34 | |||
| 35 | #[cfg(feature = "_arch")] | ||
| 36 | pub use arch::*; | ||
| 48 | 37 | ||
| 38 | pub mod raw; | ||
| 39 | |||
| 40 | mod spawner; | ||
| 41 | pub 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. | 46 | pub mod _export { |
| 51 | pub 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 | |||
| 74 | pub mod raw; | ||
| 75 | |||
| 76 | mod spawner; | ||
| 77 | pub use spawner::*; | ||
| 78 | |||
| 79 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. | ||
| 80 | #[doc(hidden)] | ||
| 81 | pub 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")] |
| 12 | mod timer_queue; | 12 | mod timer_queue; |
| 13 | pub(crate) mod util; | 13 | pub(crate) mod util; |
| 14 | #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] | ||
| 14 | mod waker; | 15 | mod waker; |
| 15 | 16 | ||
| 16 | use core::future::Future; | 17 | use core::future::Future; |
| @@ -18,11 +19,9 @@ use core::marker::PhantomData; | |||
| 18 | use core::mem; | 19 | use core::mem; |
| 19 | use core::pin::Pin; | 20 | use core::pin::Pin; |
| 20 | use core::ptr::NonNull; | 21 | use core::ptr::NonNull; |
| 21 | use core::sync::atomic::AtomicPtr; | ||
| 22 | use core::task::{Context, Poll}; | 22 | use core::task::{Context, Poll}; |
| 23 | 23 | ||
| 24 | use atomic_polyfill::{AtomicU32, Ordering}; | 24 | use atomic_polyfill::{AtomicU32, Ordering}; |
| 25 | use critical_section::CriticalSection; | ||
| 26 | #[cfg(feature = "integrated-timers")] | 25 | #[cfg(feature = "integrated-timers")] |
| 27 | use embassy_time::driver::{self, AlarmHandle}; | 26 | use 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)] | ||
| 292 | pub(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 | |||
| 301 | unsafe impl Send for PenderInner {} | ||
| 302 | unsafe 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. | ||
| 311 | pub struct Pender(pub(crate) PenderInner); | ||
| 312 | |||
| 313 | impl 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 | |||
| 329 | impl 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 | |||
| 292 | pub(crate) struct SyncExecutor { | 342 | pub(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 | ||
| 303 | impl SyncExecutor { | 352 | impl 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)] |
| 429 | pub struct Executor { | 482 | pub 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`]. |
| 498 | pub fn wake_task(task: TaskRef) { | 551 | pub 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; | |||
| 2 | use core::ptr::NonNull; | 2 | use core::ptr::NonNull; |
| 3 | 3 | ||
| 4 | use atomic_polyfill::{AtomicPtr, Ordering}; | 4 | use atomic_polyfill::{AtomicPtr, Ordering}; |
| 5 | use critical_section::CriticalSection; | ||
| 6 | 5 | ||
| 7 | use super::{TaskHeader, TaskRef}; | 6 | use 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 @@ | |||
| 1 | use core::ptr::NonNull; | ||
| 2 | use core::task::Waker; | ||
| 3 | |||
| 4 | use super::{wake_task, TaskHeader, TaskRef}; | ||
| 5 | |||
| 6 | pub(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. | ||
| 21 | pub 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] | ||
| 30 | fn _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 | |||
| 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" | 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" |
| 10 | features = ["time", "defmt"] | 10 | features = ["time", "defmt"] |
| 11 | flavors = [ | 11 | flavors = [ |
| 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] |
| 20 | sx126x = [] | 20 | sx126x = [] |
| 21 | sx127x = [] | 21 | sx127x = [] |
| 22 | stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] | 22 | stm32wl = ["dep:embassy-stm32"] |
| 23 | time = [] | 23 | time = [] |
| 24 | defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] | 24 | defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] |
| 25 | 25 | ||
| @@ -31,8 +31,8 @@ log = { version = "0.4.14", optional = true } | |||
| 31 | embassy-time = { version = "0.1.0", path = "../embassy-time" } | 31 | embassy-time = { version = "0.1.0", path = "../embassy-time" } |
| 32 | embassy-sync = { version = "0.1.0", path = "../embassy-sync" } | 32 | embassy-sync = { version = "0.1.0", path = "../embassy-sync" } |
| 33 | embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } | 33 | embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } |
| 34 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 34 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 35 | embedded-hal-async = { version = "=0.2.0-alpha.0" } | 35 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 36 | embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } | 36 | embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } |
| 37 | futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } | 37 | futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } |
| 38 | embedded-hal = { version = "0.2", features = ["unproven"] } | 38 | embedded-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" } | |||
| 87 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } | 87 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } |
| 88 | 88 | ||
| 89 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 89 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 90 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} | 90 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} |
| 91 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} | 91 | embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} |
| 92 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | 92 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } |
| 93 | 93 | ||
| 94 | defmt = { version = "0.3", optional = true } | 94 | defmt = { 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 { | |||
| 885 | mod eha { | 864 | mod 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 | ||
| 67 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 67 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 68 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} | 68 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} |
| 69 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} | 69 | embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} |
| 70 | embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} | 70 | embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} |
| 71 | 71 | ||
| 72 | paste = "1.0" | 72 | paste = "1.0" |
| 73 | pio-proc = {version= "0.2", optional = true} | 73 | pio-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)] | ||
| 22 | pub struct Config { | 23 | pub 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" | |||
| 8 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" | 8 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" |
| 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" | 9 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" |
| 10 | 10 | ||
| 11 | # TODO: sdmmc | 11 | features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] |
| 12 | # TODO: net | ||
| 13 | # TODO: subghz | ||
| 14 | features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"] | ||
| 15 | flavors = [ | 12 | flavors = [ |
| 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" } | |||
| 44 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } | 42 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } |
| 45 | 43 | ||
| 46 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 44 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 47 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} | 45 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} |
| 48 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} | 46 | embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} |
| 49 | embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} | 47 | embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} |
| 50 | 48 | ||
| 51 | embedded-storage = "0.3.0" | 49 | embedded-storage = "0.3.0" |
| 52 | 50 | ||
| @@ -60,7 +58,7 @@ sdio-host = "0.5.0" | |||
| 60 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } | 58 | embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } |
| 61 | critical-section = "1.1" | 59 | critical-section = "1.1" |
| 62 | atomic-polyfill = "1.0.1" | 60 | atomic-polyfill = "1.0.1" |
| 63 | stm32-metapac = { version = "2", features = ["rt"] } | 61 | stm32-metapac = "5" |
| 64 | vcell = "0.1.3" | 62 | vcell = "0.1.3" |
| 65 | bxcan = "0.7.0" | 63 | bxcan = "0.7.0" |
| 66 | nb = "1.0.0" | 64 | nb = "1.0.0" |
| @@ -69,15 +67,18 @@ seq-macro = "0.3.0" | |||
| 69 | cfg-if = "1.0.0" | 67 | cfg-if = "1.0.0" |
| 70 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | 68 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } |
| 71 | 69 | ||
| 70 | [dev-dependencies] | ||
| 71 | critical-section = { version = "1.1", features = ["std"] } | ||
| 72 | |||
| 72 | [build-dependencies] | 73 | [build-dependencies] |
| 73 | proc-macro2 = "1.0.36" | 74 | proc-macro2 = "1.0.36" |
| 74 | quote = "1.0.15" | 75 | quote = "1.0.15" |
| 75 | stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} | 76 | stm32-metapac = { version = "5", default-features = false, features = ["metadata"]} |
| 76 | 77 | ||
| 77 | [features] | 78 | [features] |
| 79 | default = ["stm32-metapac/rt"] | ||
| 78 | defmt = ["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"] | 80 | defmt = ["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"] |
| 79 | memory-x = ["stm32-metapac/memory-x"] | 81 | memory-x = ["stm32-metapac/memory-x"] |
| 80 | subghz = [] | ||
| 81 | exti = [] | 82 | exti = [] |
| 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" ] | |||
| 830 | stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] | 831 | stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] |
| 831 | stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] | 832 | stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] |
| 832 | stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] | 833 | stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] |
| 834 | stm32h503cb = [ "stm32-metapac/stm32h503cb" ] | ||
| 835 | stm32h503eb = [ "stm32-metapac/stm32h503eb" ] | ||
| 836 | stm32h503kb = [ "stm32-metapac/stm32h503kb" ] | ||
| 837 | stm32h503rb = [ "stm32-metapac/stm32h503rb" ] | ||
| 838 | stm32h562ag = [ "stm32-metapac/stm32h562ag" ] | ||
| 839 | stm32h562ai = [ "stm32-metapac/stm32h562ai" ] | ||
| 840 | stm32h562ig = [ "stm32-metapac/stm32h562ig" ] | ||
| 841 | stm32h562ii = [ "stm32-metapac/stm32h562ii" ] | ||
| 842 | stm32h562rg = [ "stm32-metapac/stm32h562rg" ] | ||
| 843 | stm32h562ri = [ "stm32-metapac/stm32h562ri" ] | ||
| 844 | stm32h562vg = [ "stm32-metapac/stm32h562vg" ] | ||
| 845 | stm32h562vi = [ "stm32-metapac/stm32h562vi" ] | ||
| 846 | stm32h562zg = [ "stm32-metapac/stm32h562zg" ] | ||
| 847 | stm32h562zi = [ "stm32-metapac/stm32h562zi" ] | ||
| 848 | stm32h563ag = [ "stm32-metapac/stm32h563ag" ] | ||
| 849 | stm32h563ai = [ "stm32-metapac/stm32h563ai" ] | ||
| 850 | stm32h563ig = [ "stm32-metapac/stm32h563ig" ] | ||
| 851 | stm32h563ii = [ "stm32-metapac/stm32h563ii" ] | ||
| 852 | stm32h563mi = [ "stm32-metapac/stm32h563mi" ] | ||
| 853 | stm32h563rg = [ "stm32-metapac/stm32h563rg" ] | ||
| 854 | stm32h563ri = [ "stm32-metapac/stm32h563ri" ] | ||
| 855 | stm32h563vg = [ "stm32-metapac/stm32h563vg" ] | ||
| 856 | stm32h563vi = [ "stm32-metapac/stm32h563vi" ] | ||
| 857 | stm32h563zg = [ "stm32-metapac/stm32h563zg" ] | ||
| 858 | stm32h563zi = [ "stm32-metapac/stm32h563zi" ] | ||
| 859 | stm32h573ai = [ "stm32-metapac/stm32h573ai" ] | ||
| 860 | stm32h573ii = [ "stm32-metapac/stm32h573ii" ] | ||
| 861 | stm32h573mi = [ "stm32-metapac/stm32h573mi" ] | ||
| 862 | stm32h573ri = [ "stm32-metapac/stm32h573ri" ] | ||
| 863 | stm32h573vi = [ "stm32-metapac/stm32h573vi" ] | ||
| 864 | stm32h573zi = [ "stm32-metapac/stm32h573zi" ] | ||
| 833 | stm32h723ve = [ "stm32-metapac/stm32h723ve" ] | 865 | stm32h723ve = [ "stm32-metapac/stm32h723ve" ] |
| 834 | stm32h723vg = [ "stm32-metapac/stm32h723vg" ] | 866 | stm32h723vg = [ "stm32-metapac/stm32h723vg" ] |
| 835 | stm32h723ze = [ "stm32-metapac/stm32h723ze" ] | 867 | stm32h723ze = [ "stm32-metapac/stm32h723ze" ] |
| @@ -1312,6 +1344,22 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] | |||
| 1312 | stm32l562re = [ "stm32-metapac/stm32l562re" ] | 1344 | stm32l562re = [ "stm32-metapac/stm32l562re" ] |
| 1313 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] | 1345 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] |
| 1314 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] | 1346 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] |
| 1347 | stm32u535cb = [ "stm32-metapac/stm32u535cb" ] | ||
| 1348 | stm32u535cc = [ "stm32-metapac/stm32u535cc" ] | ||
| 1349 | stm32u535ce = [ "stm32-metapac/stm32u535ce" ] | ||
| 1350 | stm32u535je = [ "stm32-metapac/stm32u535je" ] | ||
| 1351 | stm32u535nc = [ "stm32-metapac/stm32u535nc" ] | ||
| 1352 | stm32u535ne = [ "stm32-metapac/stm32u535ne" ] | ||
| 1353 | stm32u535rb = [ "stm32-metapac/stm32u535rb" ] | ||
| 1354 | stm32u535rc = [ "stm32-metapac/stm32u535rc" ] | ||
| 1355 | stm32u535re = [ "stm32-metapac/stm32u535re" ] | ||
| 1356 | stm32u535vc = [ "stm32-metapac/stm32u535vc" ] | ||
| 1357 | stm32u535ve = [ "stm32-metapac/stm32u535ve" ] | ||
| 1358 | stm32u545ce = [ "stm32-metapac/stm32u545ce" ] | ||
| 1359 | stm32u545je = [ "stm32-metapac/stm32u545je" ] | ||
| 1360 | stm32u545ne = [ "stm32-metapac/stm32u545ne" ] | ||
| 1361 | stm32u545re = [ "stm32-metapac/stm32u545re" ] | ||
| 1362 | stm32u545ve = [ "stm32-metapac/stm32u545ve" ] | ||
| 1315 | stm32u575ag = [ "stm32-metapac/stm32u575ag" ] | 1363 | stm32u575ag = [ "stm32-metapac/stm32u575ag" ] |
| 1316 | stm32u575ai = [ "stm32-metapac/stm32u575ai" ] | 1364 | stm32u575ai = [ "stm32-metapac/stm32u575ai" ] |
| 1317 | stm32u575cg = [ "stm32-metapac/stm32u575cg" ] | 1365 | stm32u575cg = [ "stm32-metapac/stm32u575cg" ] |
| @@ -1333,6 +1381,32 @@ stm32u585qi = [ "stm32-metapac/stm32u585qi" ] | |||
| 1333 | stm32u585ri = [ "stm32-metapac/stm32u585ri" ] | 1381 | stm32u585ri = [ "stm32-metapac/stm32u585ri" ] |
| 1334 | stm32u585vi = [ "stm32-metapac/stm32u585vi" ] | 1382 | stm32u585vi = [ "stm32-metapac/stm32u585vi" ] |
| 1335 | stm32u585zi = [ "stm32-metapac/stm32u585zi" ] | 1383 | stm32u585zi = [ "stm32-metapac/stm32u585zi" ] |
| 1384 | stm32u595ai = [ "stm32-metapac/stm32u595ai" ] | ||
| 1385 | stm32u595aj = [ "stm32-metapac/stm32u595aj" ] | ||
| 1386 | stm32u595qi = [ "stm32-metapac/stm32u595qi" ] | ||
| 1387 | stm32u595qj = [ "stm32-metapac/stm32u595qj" ] | ||
| 1388 | stm32u595ri = [ "stm32-metapac/stm32u595ri" ] | ||
| 1389 | stm32u595rj = [ "stm32-metapac/stm32u595rj" ] | ||
| 1390 | stm32u595vi = [ "stm32-metapac/stm32u595vi" ] | ||
| 1391 | stm32u595vj = [ "stm32-metapac/stm32u595vj" ] | ||
| 1392 | stm32u595zi = [ "stm32-metapac/stm32u595zi" ] | ||
| 1393 | stm32u595zj = [ "stm32-metapac/stm32u595zj" ] | ||
| 1394 | stm32u599bj = [ "stm32-metapac/stm32u599bj" ] | ||
| 1395 | stm32u599ni = [ "stm32-metapac/stm32u599ni" ] | ||
| 1396 | stm32u599nj = [ "stm32-metapac/stm32u599nj" ] | ||
| 1397 | stm32u599vi = [ "stm32-metapac/stm32u599vi" ] | ||
| 1398 | stm32u599vj = [ "stm32-metapac/stm32u599vj" ] | ||
| 1399 | stm32u599zi = [ "stm32-metapac/stm32u599zi" ] | ||
| 1400 | stm32u599zj = [ "stm32-metapac/stm32u599zj" ] | ||
| 1401 | stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] | ||
| 1402 | stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] | ||
| 1403 | stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] | ||
| 1404 | stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] | ||
| 1405 | stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] | ||
| 1406 | stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] | ||
| 1407 | stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] | ||
| 1408 | stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] | ||
| 1409 | stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] | ||
| 1336 | stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] | 1410 | stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] |
| 1337 | stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] | 1411 | stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] |
| 1338 | stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] | 1412 | stm32wb30ce = [ "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 _; | |||
| 3 | use std::path::PathBuf; | 3 | use std::path::PathBuf; |
| 4 | use std::{env, fs}; | 4 | use std::{env, fs}; |
| 5 | 5 | ||
| 6 | use proc_macro2::TokenStream; | 6 | use proc_macro2::{Ident, TokenStream}; |
| 7 | use quote::{format_ident, quote}; | 7 | use quote::{format_ident, quote}; |
| 8 | use stm32_metapac::metadata::METADATA; | 8 | use stm32_metapac::metadata::{MemoryRegionKind, METADATA}; |
| 9 | 9 | ||
| 10 | fn main() { | 10 | fn 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 | |||
| 941 | fn 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 | |||
| 950 | fn 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")] |
| 8 | mod _version; | 8 | mod _version; |
| 9 | 9 | ||
| 10 | #[cfg(not(any(adc_f1, adc_v1)))] | 10 | #[cfg(not(adc_f1))] |
| 11 | mod resolution; | 11 | mod resolution; |
| 12 | #[cfg(not(adc_v1))] | ||
| 13 | mod sample_time; | 12 | mod sample_time; |
| 14 | 13 | ||
| 15 | #[allow(unused)] | 14 | #[allow(unused)] |
| 16 | pub use _version::*; | 15 | pub use _version::*; |
| 17 | #[cfg(not(any(adc_f1, adc_v1)))] | 16 | #[cfg(not(adc_f1))] |
| 18 | pub use resolution::Resolution; | 17 | pub use resolution::Resolution; |
| 19 | #[cfg(not(adc_v1))] | ||
| 20 | pub use sample_time::SampleTime; | 18 | pub use sample_time::SampleTime; |
| 21 | 19 | ||
| 22 | use crate::peripherals; | 20 | use crate::peripherals; |
| 23 | 21 | ||
| 24 | #[cfg(not(adc_v1))] | ||
| 25 | pub struct Adc<'d, T: Instance> { | 22 | pub 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)))] |
| 48 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {} | 45 | pub 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))] |
| 50 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} | 47 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} |
| 51 | 48 | ||
| 52 | pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} | 49 | pub 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)] |
| 3 | pub enum Resolution { | 3 | pub enum Resolution { |
| 4 | TwelveBit, | 4 | TwelveBit, |
| @@ -19,7 +19,7 @@ pub enum Resolution { | |||
| 19 | 19 | ||
| 20 | impl Default for Resolution { | 20 | impl 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))] |
| 29 | impl_sample_time!( | 29 | impl_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 @@ | |||
| 1 | use embassy_hal_common::into_ref; | ||
| 2 | use embedded_hal_02::blocking::delay::DelayUs; | ||
| 1 | 3 | ||
| 4 | use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; | ||
| 5 | use crate::peripherals::ADC; | ||
| 6 | use crate::Peripheral; | ||
| 7 | |||
| 8 | pub const VDDA_CALIB_MV: u32 = 3300; | ||
| 9 | pub const VREF_INT: u32 = 1230; | ||
| 10 | |||
| 11 | pub struct Vbat; | ||
| 12 | impl InternalChannel<ADC> for Vbat {} | ||
| 13 | impl super::sealed::InternalChannel<ADC> for Vbat { | ||
| 14 | fn channel(&self) -> u8 { | ||
| 15 | 18 | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub struct Vref; | ||
| 20 | impl InternalChannel<ADC> for Vref {} | ||
| 21 | impl super::sealed::InternalChannel<ADC> for Vref { | ||
| 22 | fn channel(&self) -> u8 { | ||
| 23 | 17 | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | pub struct Temperature; | ||
| 28 | impl InternalChannel<ADC> for Temperature {} | ||
| 29 | impl super::sealed::InternalChannel<ADC> for Temperature { | ||
| 30 | fn channel(&self) -> u8 { | ||
| 31 | 16 | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | impl<'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}; | |||
| 9 | use super::*; | 9 | use super::*; |
| 10 | use crate::gpio::sealed::{AFType, Pin as _}; | 10 | use crate::gpio::sealed::{AFType, Pin as _}; |
| 11 | use crate::gpio::{AnyPin, Speed}; | 11 | use crate::gpio::{AnyPin, Speed}; |
| 12 | use crate::pac::{ETH, RCC, SYSCFG}; | 12 | use crate::pac::ETH; |
| 13 | use crate::Peripheral; | 13 | use crate::Peripheral; |
| 14 | 14 | ||
| 15 | const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet | 15 | const 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)))] |
| 29 | fn exticr_regs() -> pac::syscfg::Syscfg { | 29 | fn 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))] |
| 33 | fn exticr_regs() -> pac::exti::Exti { | 33 | fn 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 | ||
| 41 | pub unsafe fn on_irq() { | 41 | pub 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 @@ | |||
| 1 | use atomic_polyfill::{fence, Ordering}; | ||
| 2 | use embassy_hal_common::drop::OnDrop; | ||
| 3 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 4 | |||
| 5 | use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; | ||
| 6 | use crate::flash::FlashBank; | ||
| 7 | use crate::Peripheral; | ||
| 8 | |||
| 9 | pub struct Flash<'d> { | ||
| 10 | inner: PeripheralRef<'d, crate::peripherals::FLASH>, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<'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 | |||
| 41 | fn 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 | |||
| 52 | unsafe 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 | |||
| 85 | unsafe 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(§or) | ||
| 121 | })?; | ||
| 122 | address += sector.size; | ||
| 123 | } | ||
| 124 | Ok(()) | ||
| 125 | } | ||
| 126 | |||
| 127 | pub(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 | |||
| 152 | impl 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 | |||
| 166 | foreach_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 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::convert::TryInto; |
| 2 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 3 | 3 | ||
| 4 | use atomic_polyfill::{fence, Ordering}; | ||
| 5 | |||
| 6 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | ||
| 4 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 5 | use crate::pac; | 8 | use crate::pac; |
| 6 | 9 | ||
| 10 | pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 11 | &FLASH_REGIONS | ||
| 12 | } | ||
| 13 | |||
| 7 | pub(crate) unsafe fn lock() { | 14 | pub(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 | ||
| 16 | pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { | 23 | pub(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 = { | 29 | pub(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(); | 33 | pub(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 | ||
| 39 | pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { | 46 | pub(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 | ||
| 85 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 89 | unsafe 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; | |||
| 2 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 3 | use core::sync::atomic::{fence, Ordering}; | 3 | use core::sync::atomic::{fence, Ordering}; |
| 4 | 4 | ||
| 5 | use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; | 5 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; |
| 6 | use crate::flash::Error; | 6 | use crate::flash::Error; |
| 7 | use crate::pac; | 7 | use crate::pac; |
| 8 | 8 | ||
| 9 | const SECOND_BANK_SECTOR_START: u32 = 12; | 9 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] |
| 10 | mod alt_regions { | ||
| 11 | use embassy_hal_common::PeripheralRef; | ||
| 12 | use stm32_metapac::FLASH_SIZE; | ||
| 10 | 13 | ||
| 11 | unsafe 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))] | ||
| 93 | pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; | ||
| 94 | |||
| 95 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] | ||
| 96 | pub 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)))] | ||
| 105 | pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 106 | &FLASH_REGIONS | ||
| 26 | } | 107 | } |
| 27 | 108 | ||
| 28 | pub(crate) unsafe fn lock() { | 109 | pub(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 | ||
| 37 | pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { | 118 | pub(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 | ||
| 68 | struct FlashSector { | 127 | pub(crate) unsafe fn end_write() { |
| 69 | index: u8, | 128 | pac::FLASH.cr().write(|w| w.set_pg(false)); |
| 70 | size: u32, | ||
| 71 | } | ||
| 72 | |||
| 73 | fn 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 | ||
| 106 | pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { | 131 | pub(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 | ||
| 119 | unsafe fn erase_sector(sector: u8) -> Result<(), Error> { | 144 | pub(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 | ||
| 151 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 173 | unsafe 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)] | ||
| 200 | mod 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; | |||
| 2 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 3 | use core::sync::atomic::{fence, Ordering}; | 3 | use core::sync::atomic::{fence, Ordering}; |
| 4 | 4 | ||
| 5 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | ||
| 5 | use crate::flash::Error; | 6 | use crate::flash::Error; |
| 6 | use crate::pac; | 7 | use crate::pac; |
| 7 | 8 | ||
| 9 | pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 10 | &FLASH_REGIONS | ||
| 11 | } | ||
| 12 | |||
| 8 | pub(crate) unsafe fn lock() { | 13 | pub(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 | ||
| 17 | pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { | 22 | pub(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 = { | 31 | pub(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 | ||
| 48 | pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { | 35 | pub(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 | ||
| 71 | unsafe fn erase_sector(sector: u8) -> Result<(), Error> { | 48 | pub(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 | ||
| 110 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 87 | unsafe 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)] | ||
| 114 | mod 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 @@ | |||
| 1 | use core::convert::TryInto; | 1 | use core::convert::TryInto; |
| 2 | use core::ptr::write_volatile; | 2 | use core::ptr::write_volatile; |
| 3 | 3 | ||
| 4 | use atomic_polyfill::{fence, Ordering}; | ||
| 5 | |||
| 6 | use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; | ||
| 4 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 5 | use crate::pac; | 8 | use crate::pac; |
| 6 | 9 | ||
| 7 | const SECOND_BANK_OFFSET: usize = 0x0010_0000; | ||
| 8 | |||
| 9 | const fn is_dual_bank() -> bool { | 10 | const fn is_dual_bank() -> bool { |
| 10 | super::FLASH_SIZE / 2 > super::ERASE_SIZE | 11 | FLASH_REGIONS.len() == 2 |
| 12 | } | ||
| 13 | |||
| 14 | pub fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 15 | &FLASH_REGIONS | ||
| 11 | } | 16 | } |
| 12 | 17 | ||
| 13 | pub(crate) unsafe fn lock() { | 18 | pub(crate) unsafe fn lock() { |
| @@ -20,90 +25,64 @@ pub(crate) unsafe fn lock() { | |||
| 20 | pub(crate) unsafe fn unlock() { | 25 | pub(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 | ||
| 30 | pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { | 34 | pub(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 | |||
| 38 | pub(crate) unsafe fn end_write() {} | ||
| 39 | |||
| 40 | pub(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 | |||
| 78 | pub(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 | ||
| 103 | unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> { | 81 | pub(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 | ||
| 163 | pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { | 142 | unsafe 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 @@ | |||
| 1 | use core::convert::TryInto; | ||
| 2 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 3 | 2 | ||
| 3 | use atomic_polyfill::{fence, Ordering}; | ||
| 4 | |||
| 5 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | ||
| 4 | use crate::flash::Error; | 6 | use crate::flash::Error; |
| 5 | use crate::pac; | 7 | use crate::pac; |
| 6 | 8 | ||
| 9 | pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 10 | &FLASH_REGIONS | ||
| 11 | } | ||
| 12 | |||
| 7 | pub(crate) unsafe fn lock() { | 13 | pub(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 | ||
| 36 | pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { | 42 | pub(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 = { | 49 | pub(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 | ||
| 63 | pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { | 54 | pub(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))] | 67 | pub(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 | ||
| 114 | pub(crate) unsafe fn clear_all_err() { | 113 | pub(crate) unsafe fn clear_all_err() { |
| @@ -149,7 +148,7 @@ pub(crate) unsafe fn clear_all_err() { | |||
| 149 | }); | 148 | }); |
| 150 | } | 149 | } |
| 151 | 150 | ||
| 152 | pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { | 151 | unsafe 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 @@ | |||
| 1 | use embassy_hal_common::{into_ref, PeripheralRef}; | 1 | use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; |
| 2 | use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | ||
| 3 | 2 | ||
| 4 | pub use crate::pac::{ERASE_SIZE, ERASE_VALUE, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; | 3 | #[cfg(flash)] |
| 5 | use crate::peripherals::FLASH; | 4 | mod common; |
| 6 | use crate::Peripheral; | ||
| 7 | const 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")] | 7 | pub 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")] | ||
| 14 | mod family; | ||
| 15 | 8 | ||
| 16 | pub struct Flash<'d> { | 9 | pub use crate::_generated::flash_regions::*; |
| 17 | _inner: PeripheralRef<'d, FLASH>, | 10 | pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; |
| 18 | } | ||
| 19 | 11 | ||
| 20 | impl<'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); | 14 | pub 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; | 25 | pub 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))] | ||
| 34 | pub enum FlashBank { | ||
| 35 | Bank1 = 0, | ||
| 36 | Bank2 = 1, | ||
| 37 | Otp, | ||
| 38 | } | ||
| 68 | 39 | ||
| 69 | unsafe { | 40 | impl 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 | ||
| 82 | impl 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 | )] | ||
| 61 | mod family; | ||
| 62 | |||
| 63 | #[allow(unused_imports)] | ||
| 64 | pub 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 | ||
| 100 | impl<'d> ErrorType for Flash<'d> { | ||
| 101 | type Error = Error; | ||
| 102 | } | ||
| 103 | |||
| 104 | impl NorFlashError for Error { | 78 | impl 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 | |||
| 114 | impl<'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 | |||
| 126 | impl<'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 | |||
| 3 | use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | ||
| 4 | |||
| 5 | pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 6 | &FLASH_REGIONS | ||
| 7 | } | ||
| 8 | |||
| 9 | pub(crate) unsafe fn lock() { | ||
| 10 | unimplemented!(); | ||
| 11 | } | ||
| 12 | pub(crate) unsafe fn unlock() { | ||
| 13 | unimplemented!(); | ||
| 14 | } | ||
| 15 | pub(crate) unsafe fn begin_write() { | ||
| 16 | unimplemented!(); | ||
| 17 | } | ||
| 18 | pub(crate) unsafe fn end_write() { | ||
| 19 | unimplemented!(); | ||
| 20 | } | ||
| 21 | pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 22 | unimplemented!(); | ||
| 23 | } | ||
| 24 | pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { | ||
| 25 | unimplemented!(); | ||
| 26 | } | ||
| 27 | pub(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 | ||
| 68 | impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { | 68 | impl<'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 | ||
| 76 | impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { | 76 | impl<'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 | ||
| 84 | impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { | 84 | impl<'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 | ||
| 356 | impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { | 356 | impl<'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 | ||
| 364 | impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { | 364 | impl<'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 | ||
| 372 | impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { | 372 | impl<'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)] |
| 45 | pub mod crc; | 45 | pub 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 | ))] | ||
| 49 | pub mod flash; | 46 | pub mod flash; |
| 50 | pub mod pwm; | 47 | pub mod pwm; |
| 51 | #[cfg(quadspi)] | 48 | #[cfg(quadspi)] |
| @@ -56,6 +53,8 @@ pub mod rng; | |||
| 56 | pub mod sdmmc; | 53 | pub mod sdmmc; |
| 57 | #[cfg(spi)] | 54 | #[cfg(spi)] |
| 58 | pub mod spi; | 55 | pub mod spi; |
| 56 | #[cfg(stm32wl)] | ||
| 57 | pub mod subghz; | ||
| 59 | #[cfg(usart)] | 58 | #[cfg(usart)] |
| 60 | pub mod usart; | 59 | pub 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)] |
| 66 | pub mod wdg; | 65 | pub mod wdg; |
| 67 | 66 | ||
| 68 | #[cfg(feature = "subghz")] | ||
| 69 | pub 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. |
| 72 | pub(crate) mod _generated { | 68 | pub(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 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_common::{into_ref, PeripheralRef}; | ||
| 4 | pub use stm32_metapac::timer::vals::Ckd; | ||
| 5 | |||
| 6 | use super::simple_pwm::*; | ||
| 7 | use super::*; | ||
| 8 | #[allow(unused_imports)] | ||
| 9 | use crate::gpio::sealed::{AFType, Pin}; | ||
| 10 | use crate::gpio::AnyPin; | ||
| 11 | use crate::time::Hertz; | ||
| 12 | use crate::Peripheral; | ||
| 13 | |||
| 14 | pub struct ComplementaryPwmPin<'d, Perip, Channel> { | ||
| 15 | _pin: PeripheralRef<'d, AnyPin>, | ||
| 16 | phantom: PhantomData<(Perip, Channel)>, | ||
| 17 | } | ||
| 18 | |||
| 19 | macro_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 | |||
| 39 | complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); | ||
| 40 | complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); | ||
| 41 | complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); | ||
| 42 | complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); | ||
| 43 | |||
| 44 | pub struct ComplementaryPwm<'d, T> { | ||
| 45 | inner: PeripheralRef<'d, T>, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'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 @@ | |||
| 1 | pub mod complementary_pwm; | ||
| 1 | pub mod simple_pwm; | 2 | pub mod simple_pwm; |
| 2 | 3 | ||
| 4 | use stm32_metapac::timer::vals::Ckd; | ||
| 5 | |||
| 3 | #[cfg(feature = "unstable-pac")] | 6 | #[cfg(feature = "unstable-pac")] |
| 4 | pub mod low_level { | 7 | pub 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 | |||
| 97 | pub trait ComplementaryCaptureCompare16bitInstance: | ||
| 98 | sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static | ||
| 99 | { | ||
| 100 | } | ||
| 101 | |||
| 85 | pub trait CaptureCompare32bitInstance: | 102 | pub 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 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_common::into_ref; | ||
| 4 | use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre}; | ||
| 5 | |||
| 1 | use super::sealed::RccPeripheral; | 6 | use super::sealed::RccPeripheral; |
| 7 | use crate::gpio::sealed::AFType; | ||
| 8 | use crate::gpio::Speed; | ||
| 2 | use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; | 9 | use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; |
| 3 | use crate::pac::{FLASH, PWR, RCC}; | 10 | use crate::pac::{FLASH, PWR, RCC}; |
| 4 | use crate::rcc::{set_freqs, Clocks}; | 11 | use crate::rcc::{set_freqs, Clocks}; |
| 5 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 13 | use crate::{peripherals, Peripheral}; | ||
| 6 | 14 | ||
| 7 | /// HSI speed | 15 | /// HSI speed |
| 8 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 16 | pub 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 | ||
| 107 | pub enum McoClock { | ||
| 108 | DIV1, | ||
| 109 | DIV2, | ||
| 110 | DIV3, | ||
| 111 | DIV4, | ||
| 112 | DIV5, | ||
| 113 | } | ||
| 114 | |||
| 115 | impl 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)] | ||
| 128 | pub enum Mco1Source { | ||
| 129 | Hsi, | ||
| 130 | Lse, | ||
| 131 | Hse, | ||
| 132 | Pll, | ||
| 133 | } | ||
| 134 | |||
| 135 | impl Default for Mco1Source { | ||
| 136 | fn default() -> Self { | ||
| 137 | Self::Hsi | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | pub trait McoSource { | ||
| 142 | type Raw; | ||
| 143 | |||
| 144 | fn into_raw(&self) -> Self::Raw; | ||
| 145 | } | ||
| 146 | |||
| 147 | impl 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)] | ||
| 160 | pub enum Mco2Source { | ||
| 161 | SysClk, | ||
| 162 | Plli2s, | ||
| 163 | Hse, | ||
| 164 | Pll, | ||
| 165 | } | ||
| 166 | |||
| 167 | impl Default for Mco2Source { | ||
| 168 | fn default() -> Self { | ||
| 169 | Self::SysClk | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | impl 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 | |||
| 185 | pub(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 | |||
| 193 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 194 | |||
| 195 | pin_trait!(McoPin, McoInstance); | ||
| 196 | |||
| 197 | impl 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 | } | ||
| 217 | impl McoInstance for peripherals::MCO1 {} | ||
| 218 | |||
| 219 | impl 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 | } | ||
| 240 | impl McoInstance for peripherals::MCO2 {} | ||
| 241 | |||
| 242 | pub struct Mco<'d, T: McoInstance> { | ||
| 243 | phantom: PhantomData<&'d mut T>, | ||
| 244 | } | ||
| 245 | |||
| 246 | impl<'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 | |||
| 99 | unsafe fn flash_setup(sysclk: u32) { | 265 | unsafe 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 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; | ||
| 4 | |||
| 5 | use crate::pac::pwr::vals::Vos; | ||
| 6 | use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; | ||
| 7 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 8 | use crate::rcc::{set_freqs, Clocks}; | ||
| 9 | use crate::time::Hertz; | ||
| 10 | use crate::{peripherals, Peripheral}; | ||
| 11 | |||
| 12 | /// HSI speed | ||
| 13 | pub const HSI_FREQ: Hertz = Hertz(64_000_000); | ||
| 14 | |||
| 15 | /// CSI speed | ||
| 16 | pub const CSI_FREQ: Hertz = Hertz(4_000_000); | ||
| 17 | |||
| 18 | /// HSI48 speed | ||
| 19 | pub const HSI48_FREQ: Hertz = Hertz(48_000_000); | ||
| 20 | |||
| 21 | /// LSI speed | ||
| 22 | pub const LSI_FREQ: Hertz = Hertz(32_000); | ||
| 23 | |||
| 24 | const VCO_MIN: u32 = 150_000_000; | ||
| 25 | const VCO_MAX: u32 = 420_000_000; | ||
| 26 | const VCO_WIDE_MIN: u32 = 128_000_000; | ||
| 27 | const 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)] | ||
| 34 | pub 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 | |||
| 45 | pub 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 | |||
| 54 | pub struct Hse { | ||
| 55 | /// HSE frequency. | ||
| 56 | pub freq: Hertz, | ||
| 57 | /// HSE mode. | ||
| 58 | pub mode: HseMode, | ||
| 59 | } | ||
| 60 | |||
| 61 | pub 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 | |||
| 72 | pub 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 | |||
| 83 | pub enum PllSource { | ||
| 84 | Hsi, | ||
| 85 | Csi, | ||
| 86 | Hse, | ||
| 87 | } | ||
| 88 | |||
| 89 | pub 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)] | ||
| 110 | pub enum AHBPrescaler { | ||
| 111 | NotDivided, | ||
| 112 | Div2, | ||
| 113 | Div4, | ||
| 114 | Div8, | ||
| 115 | Div16, | ||
| 116 | Div64, | ||
| 117 | Div128, | ||
| 118 | Div256, | ||
| 119 | Div512, | ||
| 120 | } | ||
| 121 | |||
| 122 | impl 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)] | ||
| 140 | pub enum APBPrescaler { | ||
| 141 | NotDivided, | ||
| 142 | Div2, | ||
| 143 | Div4, | ||
| 144 | Div8, | ||
| 145 | Div16, | ||
| 146 | } | ||
| 147 | |||
| 148 | impl 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)] | ||
| 182 | pub enum TimerPrescaler { | ||
| 183 | DefaultX2, | ||
| 184 | DefaultX4, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl 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 | |||
| 196 | impl 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 | |||
| 208 | impl 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] | ||
| 226 | pub 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 | |||
| 247 | impl 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 | |||
| 271 | pub(crate) mod sealed { | ||
| 272 | pub trait McoInstance { | ||
| 273 | type Source; | ||
| 274 | unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 279 | |||
| 280 | pin_trait!(McoPin, McoInstance); | ||
| 281 | |||
| 282 | macro_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 | |||
| 299 | impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); | ||
| 300 | impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); | ||
| 301 | |||
| 302 | pub struct Mco<'d, T: McoInstance> { | ||
| 303 | phantom: PhantomData<&'d mut T>, | ||
| 304 | } | ||
| 305 | |||
| 306 | impl<'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 | |||
| 316 | pub(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 | |||
| 451 | struct PllInput { | ||
| 452 | hsi: Option<Hertz>, | ||
| 453 | hse: Option<Hertz>, | ||
| 454 | csi: Option<Hertz>, | ||
| 455 | } | ||
| 456 | |||
| 457 | struct PllOutput { | ||
| 458 | p: Option<Hertz>, | ||
| 459 | #[allow(dead_code)] | ||
| 460 | q: Option<Hertz>, | ||
| 461 | #[allow(dead_code)] | ||
| 462 | r: Option<Hertz>, | ||
| 463 | } | ||
| 464 | |||
| 465 | unsafe 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 | |||
| 554 | fn 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 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | |||
| 3 | use embassy_hal_common::into_ref; | ||
| 4 | use stm32_metapac::rcc::vals::{Mcopre, Mcosel}; | ||
| 5 | |||
| 6 | use crate::gpio::sealed::AFType; | ||
| 7 | use crate::gpio::Speed; | ||
| 1 | use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; | 8 | use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; |
| 2 | use crate::pac::{FLASH, RCC}; | 9 | use crate::pac::{FLASH, RCC}; |
| 3 | use crate::rcc::{set_freqs, Clocks}; | 10 | use crate::rcc::{set_freqs, Clocks}; |
| 4 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 12 | use crate::{peripherals, Peripheral}; | ||
| 5 | 13 | ||
| 6 | /// HSI speed | 14 | /// HSI speed |
| 7 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 15 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| @@ -298,6 +306,131 @@ impl Default for Config { | |||
| 298 | } | 306 | } |
| 299 | } | 307 | } |
| 300 | 308 | ||
| 309 | pub enum McoClock { | ||
| 310 | DIV1, | ||
| 311 | DIV2, | ||
| 312 | DIV4, | ||
| 313 | DIV8, | ||
| 314 | DIV16, | ||
| 315 | } | ||
| 316 | |||
| 317 | impl 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)] | ||
| 330 | pub 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 | |||
| 343 | impl Default for Mco1Source { | ||
| 344 | fn default() -> Self { | ||
| 345 | Self::Hsi16 | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | pub trait McoSource { | ||
| 350 | type Raw; | ||
| 351 | |||
| 352 | fn into_raw(&self) -> Self::Raw; | ||
| 353 | } | ||
| 354 | |||
| 355 | impl 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 | |||
| 373 | pub(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 | |||
| 381 | pub trait McoInstance: sealed::McoInstance + 'static {} | ||
| 382 | |||
| 383 | pin_trait!(McoPin, McoInstance); | ||
| 384 | |||
| 385 | impl 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 | |||
| 409 | impl McoInstance for peripherals::MCO {} | ||
| 410 | |||
| 411 | pub struct Mco<'d, T: McoInstance> { | ||
| 412 | phantom: PhantomData<&'d mut T>, | ||
| 413 | } | ||
| 414 | |||
| 415 | impl<'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 | |||
| 301 | pub(crate) unsafe fn init(config: Config) { | 434 | pub(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")] | ||
| 24 | mod _version; | 25 | mod _version; |
| 25 | pub use _version::*; | 26 | pub 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)))] |
| 623 | use vals::Br; | 623 | use vals::Br; |
| 624 | #[cfg(any(spi_v3, spi_v4))] | 624 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 625 | use vals::Mbr as Br; | 625 | use vals::Mbr as Br; |
| 626 | 626 | ||
| 627 | fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { | 627 | fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { |
| @@ -647,17 +647,17 @@ trait RegsExt { | |||
| 647 | 647 | ||
| 648 | impl RegsExt for Regs { | 648 | impl 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 | ||
| 727 | fn flush_rx_fifo(regs: Regs) { | 727 | fn 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 | ||
| 740 | fn set_txdmaen(regs: Regs, val: bool) { | 740 | fn 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 | ||
| 753 | fn set_rxdmaen(regs: Regs, val: bool) { | 753 | fn 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 | ||
| 3 | use 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))] |
| 6 | pub struct Hertz(pub u32); | 8 | pub struct Hertz(pub u32); |
| 7 | 9 | ||
| @@ -33,3 +35,45 @@ pub fn khz(kilohertz: u32) -> Hertz { | |||
| 33 | pub fn mhz(megahertz: u32) -> Hertz { | 35 | pub fn mhz(megahertz: u32) -> Hertz { |
| 34 | Hertz::mhz(megahertz) | 36 | Hertz::mhz(megahertz) |
| 35 | } | 37 | } |
| 38 | |||
| 39 | impl Mul<u32> for Hertz { | ||
| 40 | type Output = Hertz; | ||
| 41 | fn mul(self, rhs: u32) -> Self::Output { | ||
| 42 | Hertz(self.0 * rhs) | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | impl Div<u32> for Hertz { | ||
| 47 | type Output = Hertz; | ||
| 48 | fn div(self, rhs: u32) -> Self::Output { | ||
| 49 | Hertz(self.0 / rhs) | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | impl Mul<u16> for Hertz { | ||
| 54 | type Output = Hertz; | ||
| 55 | fn mul(self, rhs: u16) -> Self::Output { | ||
| 56 | self * (rhs as u32) | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | impl Div<u16> for Hertz { | ||
| 61 | type Output = Hertz; | ||
| 62 | fn div(self, rhs: u16) -> Self::Output { | ||
| 63 | self / (rhs as u32) | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Mul<u8> for Hertz { | ||
| 68 | type Output = Hertz; | ||
| 69 | fn mul(self, rhs: u8) -> Self::Output { | ||
| 70 | self * (rhs as u32) | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | impl 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 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::future::poll_fn; | 1 | use core::future::poll_fn; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 2 | use core::slice; |
| 4 | use core::task::Poll; | 3 | use core::task::Poll; |
| 5 | 4 | ||
| 6 | use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; | 5 | use embassy_cortex_m::interrupt::Interrupt; |
| 7 | use embassy_hal_common::ring_buffer::RingBuffer; | 6 | use embassy_hal_common::atomic_ring_buffer::RingBuffer; |
| 8 | use embassy_sync::waitqueue::WakerRegistration; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
| 9 | 8 | ||
| 10 | use super::*; | 9 | use super::*; |
| 11 | 10 | ||
| 12 | pub struct State<'d, T: BasicInstance>(StateStorage<StateInner<'d, T>>); | 11 | pub struct State { |
| 13 | impl<'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 | |||
| 19 | struct 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 | ||
| 29 | unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} | 19 | impl State { |
| 30 | unsafe 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 | ||
| 32 | pub struct BufferedUart<'d, T: BasicInstance> { | 30 | pub 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 | ||
| 36 | pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { | 35 | pub struct BufferedUartTx<'d, T: BasicInstance> { |
| 37 | inner: &'u BufferedUart<'d, T>, | 36 | phantom: PhantomData<&'d mut T>, |
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { | 39 | pub struct BufferedUartRx<'d, T: BasicInstance> { |
| 41 | inner: &'u BufferedUart<'d, T>, | 40 | phantom: PhantomData<&'d mut T>, |
| 42 | } | 41 | } |
| 43 | 42 | ||
| 44 | impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} | ||
| 45 | |||
| 46 | impl<'d, T: BasicInstance> BufferedUart<'d, T> { | 43 | impl<'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> { | 163 | impl<'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> { | 239 | impl<'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> { | 309 | impl<'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 | ||
| 340 | impl<'d, T: BasicInstance> StateInner<'d, T> | 324 | impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { |
| 341 | where | 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() { | 339 | unsafe 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 | ||
| 409 | impl<'d, T: BasicInstance> PeripheralState for StateInner<'d, T> | 377 | if sr.idle() { |
| 410 | where | 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 | ||
| 430 | impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { | 414 | impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> { |
| 431 | type Error = Error; | 415 | type Error = Error; |
| 432 | } | 416 | } |
| 433 | 417 | ||
| 434 | impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { | 418 | impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> { |
| 435 | type Error = Error; | 419 | type Error = Error; |
| 436 | } | 420 | } |
| 437 | 421 | ||
| 438 | impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { | 422 | impl<'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 | ||
| 444 | impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { | 428 | impl<'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 | ||
| 450 | impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { | 434 | impl<'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 | ||
| 460 | impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { | 444 | impl<'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 | ||
| 470 | impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { | 454 | impl<'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 | ||
| 480 | impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { | 464 | impl<'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 | ||
| 490 | impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { | 474 | impl<'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 | ||
| 496 | impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { | 480 | impl<'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 | ||
| 502 | impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { | 486 | impl<'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 | ||
| 512 | impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { | 496 | impl<'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 | |||
| 506 | mod 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")] | ||
| 585 | mod 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 | ))] | ||
| 658 | mod 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; | |||
| 12 | use embassy_usb_driver::{ | 12 | use embassy_usb_driver::{ |
| 13 | Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, | 13 | Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, |
| 14 | }; | 14 | }; |
| 15 | use pac::common::{Reg, RW}; | ||
| 16 | use pac::usb::vals::{EpType, Stat}; | ||
| 17 | 15 | ||
| 18 | use super::{DmPin, DpPin, Instance}; | 16 | use super::{DmPin, DpPin, Instance}; |
| 19 | use crate::gpio::sealed::AFType; | 17 | use crate::gpio::sealed::AFType; |
| 20 | use crate::interrupt::InterruptExt; | 18 | use crate::interrupt::InterruptExt; |
| 21 | use crate::pac::usb::regs; | 19 | use crate::pac::usb::regs; |
| 20 | use crate::pac::usb::vals::{EpType, Stat}; | ||
| 21 | use crate::pac::USBRAM; | ||
| 22 | use crate::rcc::sealed::RccPeripheral; | 22 | use crate::rcc::sealed::RccPeripheral; |
| 23 | use crate::{pac, Peripheral}; | 23 | use crate::Peripheral; |
| 24 | 24 | ||
| 25 | const EP_COUNT: usize = 8; | 25 | const EP_COUNT: usize = 8; |
| 26 | 26 | ||
| 27 | #[cfg(any(usb_v1_x1, usb_v1_x2))] | 27 | #[cfg(any(usbram_16x1_512, usbram_16x2_512))] |
| 28 | const EP_MEMORY_SIZE: usize = 512; | 28 | const USBRAM_SIZE: usize = 512; |
| 29 | #[cfg(not(any(usb_v1_x1, usb_v1_x2)))] | 29 | #[cfg(usbram_16x2_1024)] |
| 30 | const EP_MEMORY_SIZE: usize = 1024; | 30 | const USBRAM_SIZE: usize = 1024; |
| 31 | #[cfg(usbram_32_2048)] | ||
| 32 | const USBRAM_SIZE: usize = 2048; | ||
| 33 | |||
| 34 | #[cfg(not(usbram_32_2048))] | ||
| 35 | const USBRAM_ALIGN: usize = 2; | ||
| 36 | #[cfg(usbram_32_2048)] | ||
| 37 | const USBRAM_ALIGN: usize = 4; | ||
| 31 | 38 | ||
| 32 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 39 | const NEW_AW: AtomicWaker = AtomicWaker::new(); |
| 33 | static BUS_WAKER: AtomicWaker = NEW_AW; | 40 | static 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 | ||
| 67 | fn 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) |
| 61 | fn calc_out_len(len: u16) -> (u16, u16) { | 72 | fn 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 | } |
| 68 | fn 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 | } | 82 | mod btable { |
| 71 | fn 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) { |
| 74 | fn 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 | } |
| 77 | fn ep_out_len<T: Instance>(index: usize) -> Reg<u16, RW> { | 102 | #[cfg(usbram_32_2048)] |
| 78 | T::regs().ep_mem(index * 4 + 3) | 103 | mod 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 | ||
| 81 | struct EndpointBuffer<T: Instance> { | 123 | struct EndpointBuffer<T: Instance> { |
| @@ -87,23 +129,25 @@ struct EndpointBuffer<T: Instance> { | |||
| 87 | impl<T: Instance> EndpointBuffer<T> { | 129 | impl<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] |
| 26 | nightly = ["embedded-io/async"] | 26 | nightly = ["embedded-io/async"] |
| 27 | std = [] | 27 | std = [] |
| 28 | turbowakers = [] | ||
| 28 | 29 | ||
| 29 | [dependencies] | 30 | [dependencies] |
| 30 | defmt = { version = "0.3", optional = true } | 31 | defmt = { 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 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::task::Waker; | ||
| 3 | |||
| 4 | use crate::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 5 | use crate::blocking_mutex::Mutex; | ||
| 6 | |||
| 7 | /// Utility struct to register and wake a waker. | ||
| 8 | pub struct AtomicWaker { | ||
| 9 | waker: Mutex<CriticalSectionRawMutex, Cell<Option<Waker>>>, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl 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 @@ | |||
| 1 | use core::ptr; | ||
| 2 | use core::ptr::NonNull; | ||
| 3 | use core::sync::atomic::{AtomicPtr, Ordering}; | ||
| 4 | use core::task::Waker; | ||
| 5 | |||
| 6 | /// Utility struct to register and wake a waker. | ||
| 7 | pub struct AtomicWaker { | ||
| 8 | waker: AtomicPtr<()>, | ||
| 9 | } | ||
| 10 | |||
| 11 | impl 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 | ||
| 3 | mod waker; | 3 | #[cfg_attr(feature = "turbowakers", path = "atomic_waker_turbo.rs")] |
| 4 | pub use waker::*; | 4 | mod atomic_waker; |
| 5 | pub use atomic_waker::*; | ||
| 6 | |||
| 7 | mod waker_registration; | ||
| 8 | pub use waker_registration::*; | ||
| 5 | 9 | ||
| 6 | mod multi_waker; | 10 | mod multi_waker; |
| 7 | pub use multi_waker::*; | 11 | pub 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 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::mem; | 1 | use core::mem; |
| 3 | use core::task::Waker; | 2 | use core::task::Waker; |
| 4 | 3 | ||
| 5 | use crate::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 6 | use 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)] |
| 10 | pub struct WakerRegistration { | 6 | pub 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. | ||
| 59 | pub struct AtomicWaker { | ||
| 60 | waker: Mutex<CriticalSectionRawMutex, Cell<Option<Waker>>>, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl 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 } | |||
| 152 | log = { version = "0.4.14", optional = true } | 152 | log = { version = "0.4.14", optional = true } |
| 153 | 153 | ||
| 154 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } | 154 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } |
| 155 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} | 155 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} |
| 156 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} | 156 | embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} |
| 157 | 157 | ||
| 158 | futures-util = { version = "0.3.17", default-features = false } | 158 | futures-util = { version = "0.3.17", default-features = false } |
| 159 | embassy-sync = { version = "0.1", path = "../embassy-sync" } | 159 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } |
| 11 | embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } | 11 | embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } |
| 12 | embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } |
| 11 | embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } | 11 | embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } |
| 12 | embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } |
| 12 | embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } | 12 | embassy-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")] |
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_boot_rp::*; | 7 | use embassy_boot_rp::*; |
| 8 | use embassy_rp::flash::ERASE_SIZE; | ||
| 9 | use embassy_time::Duration; | 8 | use embassy_time::Duration; |
| 10 | 9 | ||
| 11 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | 10 | const 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")] |
| 6 | use defmt_rtt as _; | 6 | use defmt_rtt as _; |
| 7 | use embassy_boot_stm32::*; | 7 | use embassy_boot_stm32::*; |
| 8 | use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; | 8 | use embassy_stm32::flash::Flash; |
| 9 | 9 | ||
| 10 | #[entry] | 10 | #[entry] |
| 11 | fn main() -> ! { | 11 | fn 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] |
| 19 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } | 19 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } |
| 20 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } | 20 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } |
| 21 | embassy-time = { version = "0.1.0", path = "../../embassy-time" } | 21 | embassy-time = { version = "0.1.0", path = "../../embassy-time" } |
| 22 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 22 | embassy-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] |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 14 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 14 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 15 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 15 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 16 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 16 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 17 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 17 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } |
| 18 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } | 18 | embassy-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" } | |||
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ | 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ |
| 10 | "defmt", | 10 | "defmt", |
| 11 | ] } | 11 | ] } |
| 12 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ | 12 | embassy-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"))'] |
| 2 | runner = "probe-run --chip RP2040" | 2 | runner = "probe-rs-cli run --chip RP2040" |
| 3 | 3 | ||
| 4 | [build] | 4 | [build] |
| 5 | target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ | 5 | target = "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] |
| 9 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } | ||
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } | 13 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| @@ -30,8 +31,8 @@ display-interface = "0.4.1" | |||
| 30 | byte-slice-cast = { version = "1.2.0", default-features = false } | 31 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 31 | smart-leds = "0.3.0" | 32 | smart-leds = "0.3.0" |
| 32 | 33 | ||
| 33 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 34 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 34 | embedded-hal-async = "0.2.0-alpha.0" | 35 | embedded-hal-async = "0.2.0-alpha.1" |
| 35 | embedded-io = { version = "0.4.0", features = ["async", "defmt"] } | 36 | embedded-io = { version = "0.4.0", features = ["async", "defmt"] } |
| 36 | embedded-storage = { version = "0.3" } | 37 | embedded-storage = { version = "0.3" } |
| 37 | static_cell = "1.0.0" | 38 | static_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 @@ | |||
| 5 | use core::cell::RefCell; | 5 | use core::cell::RefCell; |
| 6 | 6 | ||
| 7 | use defmt::*; | 7 | use defmt::*; |
| 8 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | ||
| 8 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::gpio::{Level, Output}; | 10 | use embassy_rp::gpio::{Level, Output}; |
| 10 | use embassy_rp::spi; | 11 | use embassy_rp::spi; |
| 11 | use embassy_rp::spi::{Blocking, Spi}; | 12 | use embassy_rp::spi::{Blocking, Spi}; |
| 13 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 14 | use embassy_sync::blocking_mutex::Mutex; | ||
| 12 | use embassy_time::Delay; | 15 | use embassy_time::Delay; |
| 13 | use embedded_graphics::image::{Image, ImageRawLE}; | 16 | use embedded_graphics::image::{Image, ImageRawLE}; |
| 14 | use embedded_graphics::mono_font::ascii::FONT_10X20; | 17 | use embedded_graphics::mono_font::ascii::FONT_10X20; |
| @@ -21,10 +24,9 @@ use st7789::{Orientation, ST7789}; | |||
| 21 | use {defmt_rtt as _, panic_probe as _}; | 24 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 25 | ||
| 23 | use crate::my_display_interface::SPIDeviceInterface; | 26 | use crate::my_display_interface::SPIDeviceInterface; |
| 24 | use crate::shared_spi::SpiDeviceWithCs; | ||
| 25 | use crate::touch::Touch; | 27 | use crate::touch::Touch; |
| 26 | 28 | ||
| 27 | //const DISPLAY_FREQ: u32 = 64_000_000; | 29 | const DISPLAY_FREQ: u32 = 64_000_000; |
| 28 | const TOUCH_FREQ: u32 = 200_000; | 30 | const 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 | ||
| 107 | mod 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 |
| 184 | mod touch { | 114 | mod 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 { | |||
| 247 | mod my_display_interface { | 175 | mod 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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } |
| 11 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } | 11 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } |
| 12 | embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } | 11 | embassy-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" | |||
| 13 | defmt-rtt = "0.4" | 13 | defmt-rtt = "0.4" |
| 14 | panic-probe = "0.3" | 14 | panic-probe = "0.3" |
| 15 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 15 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 16 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 16 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 17 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 17 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 18 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } | 18 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } |
| 19 | static_cell = "1.0" | 19 | static_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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 8 | use embassy_time::{Delay, Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async 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; | |||
| 62 | use cortex_m::peripheral::NVIC; | 62 | use cortex_m::peripheral::NVIC; |
| 63 | use cortex_m_rt::entry; | 63 | use cortex_m_rt::entry; |
| 64 | use defmt::*; | 64 | use defmt::*; |
| 65 | use embassy_stm32::executor::{Executor, InterruptExecutor}; | 65 | use embassy_executor::{Executor, InterruptExecutor}; |
| 66 | use embassy_stm32::interrupt; | 66 | use embassy_stm32::interrupt; |
| 67 | use embassy_stm32::pac::Interrupt; | 67 | use embassy_stm32::pac::Interrupt; |
| 68 | use embassy_time::{Duration, Instant, Timer}; | 68 | use 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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 13 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-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; | |||
| 62 | use cortex_m::peripheral::NVIC; | 62 | use cortex_m::peripheral::NVIC; |
| 63 | use cortex_m_rt::entry; | 63 | use cortex_m_rt::entry; |
| 64 | use defmt::*; | 64 | use defmt::*; |
| 65 | use embassy_stm32::executor::{Executor, InterruptExecutor}; | 65 | use embassy_executor::{Executor, InterruptExecutor}; |
| 66 | use embassy_stm32::interrupt; | 66 | use embassy_stm32::interrupt; |
| 67 | use embassy_stm32::pac::Interrupt; | 67 | use embassy_stm32::pac::Interrupt; |
| 68 | use embassy_time::{Duration, Instant, Timer}; | 68 | use 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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-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"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-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 @@ | |||
| 5 | use defmt::{info, unwrap}; | 5 | use defmt::{info, unwrap}; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::flash::Flash; | 7 | use embassy_stm32::flash::Flash; |
| 8 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async 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; | |||
| 62 | use cortex_m::peripheral::NVIC; | 62 | use cortex_m::peripheral::NVIC; |
| 63 | use cortex_m_rt::entry; | 63 | use cortex_m_rt::entry; |
| 64 | use defmt::*; | 64 | use defmt::*; |
| 65 | use embassy_stm32::executor::{Executor, InterruptExecutor}; | 65 | use embassy_executor::{Executor, InterruptExecutor}; |
| 66 | use embassy_stm32::interrupt; | 66 | use embassy_stm32::interrupt; |
| 67 | use embassy_stm32::pac::Interrupt; | 67 | use embassy_stm32::pac::Interrupt; |
| 68 | use embassy_time::{Duration, Instant, Timer}; | 68 | use 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin}; | ||
| 8 | use embassy_stm32::pwm::simple_pwm::PwmPin; | ||
| 9 | use embassy_stm32::pwm::Channel; | ||
| 10 | use embassy_stm32::time::khz; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async 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 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::interrupt; | 7 | use embassy_stm32::interrupt; |
| 8 | use embassy_stm32::usart::{BufferedUart, Config, State}; | 8 | use embassy_stm32::usart::{BufferedUart, Config}; |
| 9 | use embedded_io::asynch::BufRead; | 9 | use embedded_io::asynch::BufRead; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } |
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } | 11 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } |
| 12 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } | 12 | embassy-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] | ||
| 2 | runner = 'probe-rs-cli run --chip STM32H563ZITx' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv8m.main-none-eabihf" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_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] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32h5-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | ||
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | ||
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } | ||
| 13 | embedded-io = { version = "0.4.0", features = ["async"] } | ||
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | |||
| 16 | defmt = "0.3" | ||
| 17 | defmt-rtt = "0.4" | ||
| 18 | |||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 20 | cortex-m-rt = "0.7.0" | ||
| 21 | embedded-hal = "0.2.6" | ||
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } | ||
| 23 | embedded-hal-async = { version = "=0.2.0-alpha.1" } | ||
| 24 | embedded-nal-async = "0.4.0" | ||
| 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 27 | heapless = { version = "0.7.5", default-features = false } | ||
| 28 | rand_core = "0.6.3" | ||
| 29 | critical-section = "1.1" | ||
| 30 | micromath = "2.0.0" | ||
| 31 | stm32-fmc = "0.2.4" | ||
| 32 | embedded-storage = "0.3.0" | ||
| 33 | static_cell = "1.0" | ||
| 34 | |||
| 35 | # cargo build/run | ||
| 36 | [profile.dev] | ||
| 37 | codegen-units = 1 | ||
| 38 | debug = 2 | ||
| 39 | debug-assertions = true # <- | ||
| 40 | incremental = false | ||
| 41 | opt-level = 3 # <- | ||
| 42 | overflow-checks = true # <- | ||
| 43 | |||
| 44 | # cargo test | ||
| 45 | [profile.test] | ||
| 46 | codegen-units = 1 | ||
| 47 | debug = 2 | ||
| 48 | debug-assertions = true # <- | ||
| 49 | incremental = false | ||
| 50 | opt-level = 3 # <- | ||
| 51 | overflow-checks = true # <- | ||
| 52 | |||
| 53 | # cargo build/run --release | ||
| 54 | [profile.release] | ||
| 55 | codegen-units = 1 | ||
| 56 | debug = 2 | ||
| 57 | debug-assertions = false # <- | ||
| 58 | incremental = false | ||
| 59 | lto = 'fat' | ||
| 60 | opt-level = 3 # <- | ||
| 61 | overflow-checks = false # <- | ||
| 62 | |||
| 63 | # cargo test --release | ||
| 64 | [profile.bench] | ||
| 65 | codegen-units = 1 | ||
| 66 | debug = 2 | ||
| 67 | debug-assertions = false # <- | ||
| 68 | incremental = false | ||
| 69 | lto = 'fat' | ||
| 70 | opt-level = 3 # <- | ||
| 71 | overflow-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 @@ | |||
| 1 | fn 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 @@ | |||
| 1 | MEMORY | ||
| 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::exti::ExtiInput; | ||
| 8 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_net::tcp::TcpSocket; | ||
| 8 | use embassy_net::{Ipv4Address, Stack, StackResources}; | ||
| 9 | use embassy_stm32::eth::generic_smi::GenericSMI; | ||
| 10 | use embassy_stm32::eth::{Ethernet, PacketQueue}; | ||
| 11 | use embassy_stm32::peripherals::ETH; | ||
| 12 | use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; | ||
| 13 | use embassy_stm32::rng::Rng; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_stm32::{interrupt, Config}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use embedded_io::asynch::Write; | ||
| 18 | use rand_core::RngCore; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | macro_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 | |||
| 31 | type Device = Ethernet<'static, ETH, GenericSMI>; | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 35 | stack.run().await | ||
| 36 | } | ||
| 37 | |||
| 38 | #[embassy_executor::main] | ||
| 39 | async 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; | ||
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::time::Hertz; | ||
| 10 | use embassy_time::Duration; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | const ADDRESS: u8 = 0x5F; | ||
| 14 | const WHOAMI: u8 = 0x0F; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rng::Rng; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async 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 | |||
| 5 | use cortex_m_rt::entry; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Executor; | ||
| 8 | use embassy_stm32::dma::NoDma; | ||
| 9 | use embassy_stm32::interrupt; | ||
| 10 | use embassy_stm32::usart::{Config, Uart}; | ||
| 11 | use static_cell::StaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::task] | ||
| 15 | async 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 | |||
| 32 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 33 | |||
| 34 | #[entry] | ||
| 35 | fn 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 | |||
| 5 | use core::fmt::Write; | ||
| 6 | |||
| 7 | use cortex_m_rt::entry; | ||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Executor; | ||
| 10 | use embassy_stm32::dma::NoDma; | ||
| 11 | use embassy_stm32::interrupt; | ||
| 12 | use embassy_stm32::usart::{Config, Uart}; | ||
| 13 | use heapless::String; | ||
| 14 | use static_cell::StaticCell; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::task] | ||
| 18 | async 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 | |||
| 35 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 36 | |||
| 37 | #[entry] | ||
| 38 | fn 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::dma::NoDma; | ||
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; | ||
| 10 | use embassy_stm32::usart::{Config, Uart, UartRx}; | ||
| 11 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 12 | use embassy_sync::channel::Channel; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::task] | ||
| 16 | async 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 | |||
| 27 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async 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] | ||
| 51 | async 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 | |||
| 5 | use defmt::{panic, *}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 10 | use embassy_stm32::{interrupt, pac, Config}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::Builder; | ||
| 14 | use futures::future::join; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async 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 | |||
| 109 | struct Disconnected {} | ||
| 110 | |||
| 111 | impl 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 | |||
| 120 | async 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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } |
| 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } | 12 | embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } |
| @@ -19,8 +19,8 @@ defmt-rtt = "0.4" | |||
| 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 19 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 20 | cortex-m-rt = "0.7.0" | 20 | cortex-m-rt = "0.7.0" |
| 21 | embedded-hal = "0.2.6" | 21 | embedded-hal = "0.2.6" |
| 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 22 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 23 | embedded-hal-async = { version = "=0.2.0-alpha.0" } | 23 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 24 | embedded-nal-async = "0.4.0" | 24 | embedded-nal-async = "0.4.0" |
| 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 25 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 26 | futures = { 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] |
| 12 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 12 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 13 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 13 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 14 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 14 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 15 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 15 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 16 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} | 16 | embassy-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 @@ | |||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_stm32::interrupt; | 7 | use embassy_stm32::interrupt; |
| 8 | use embassy_stm32::usart::{BufferedUart, Config, State}; | 8 | use embassy_stm32::usart::{BufferedUart, Config}; |
| 9 | use embedded_io::asynch::{Read, Write}; | 9 | use embedded_io::asynch::{Read, Write}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } | 11 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | 11 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } |
| 12 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } | 12 | embassy-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" | |||
| 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 18 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 19 | cortex-m-rt = "0.7.0" | 19 | cortex-m-rt = "0.7.0" |
| 20 | embedded-hal = "0.2.6" | 20 | embedded-hal = "0.2.6" |
| 21 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 21 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 22 | embedded-hal-async = { version = "=0.2.0-alpha.0" } | 22 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 25 | heapless = { version = "0.7.5", default-features = false } | 25 | heapless = { 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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 8 | use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async 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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } |
| 12 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 12 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } | 11 | embassy-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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } | 11 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } |
| 12 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } | 12 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } |
| 13 | 13 | ||
| 14 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } | 14 | lorawan-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] |
| 11 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } | 11 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } |
| 12 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] } | 12 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } |
| 13 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } | 13 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } |
| 14 | 14 | ||
| 15 | wasm-logger = "0.2.0" | 15 | wasm-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] |
| 4 | channel = "nightly-2023-02-07" | 4 | channel = "nightly-2023-04-02" |
| 5 | components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] | 5 | components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] |
| 6 | targets = [ | 6 | targets = [ |
| 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] |
| 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } | 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } |
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } | 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | 12 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 13 | embedded-io = { version = "0.4.0", features = ["async"] } | 13 | embedded-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 | |||
| 5 | use defmt::{assert, info}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_time::{Duration, Instant, Timer}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async 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] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } | 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } |
| 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } | 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } |
| 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 12 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| @@ -17,8 +17,8 @@ defmt-rtt = "0.4" | |||
| 17 | cortex-m = { version = "0.7.6" } | 17 | cortex-m = { version = "0.7.6" } |
| 18 | cortex-m-rt = "0.7.0" | 18 | cortex-m-rt = "0.7.0" |
| 19 | embedded-hal = "0.2.6" | 19 | embedded-hal = "0.2.6" |
| 20 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 20 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 21 | embedded-hal-async = { version = "=0.2.0-alpha.0" } | 21 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 22 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } | 22 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } |
| 23 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 23 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 24 | embedded-io = { version = "0.4.0", features = ["async"] } | 24 | embedded-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 | |||
| 11 | stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo | 11 | stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo |
| 12 | stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo | 12 | stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo |
| 13 | stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo | 13 | stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo |
| 14 | stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo | ||
| 14 | stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board | 15 | stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board |
| 15 | 16 | ||
| 16 | [dependencies] | 17 | [dependencies] |
| 17 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 18 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 18 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 19 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 19 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } | 20 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } |
| 20 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } | 21 | embassy-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" | |||
| 25 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 26 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 26 | cortex-m-rt = "0.7.0" | 27 | cortex-m-rt = "0.7.0" |
| 27 | embedded-hal = "0.2.6" | 28 | embedded-hal = "0.2.6" |
| 28 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 29 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 29 | embedded-hal-async = { version = "=0.2.0-alpha.0" } | 30 | embedded-hal-async = { version = "=0.2.0-alpha.1" } |
| 30 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } | 31 | panic-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); |
