aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/modules/ROOT/examples/basic/Cargo.toml2
-rw-r--r--docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml2
-rw-r--r--embassy-boot/boot/src/boot_loader.rs43
-rw-r--r--embassy-boot/boot/src/firmware_updater.rs34
-rw-r--r--embassy-boot/boot/src/lib.rs159
-rw-r--r--embassy-boot/boot/src/mem_flash.rs156
-rw-r--r--embassy-boot/boot/src/partition.rs40
-rw-r--r--embassy-cortex-m/src/executor.rs116
-rw-r--r--embassy-cortex-m/src/lib.rs2
-rw-r--r--embassy-executor/Cargo.toml25
-rw-r--r--embassy-executor/src/arch/cortex_m.rs238
-rw-r--r--embassy-executor/src/arch/riscv32.rs133
-rw-r--r--embassy-executor/src/arch/std.rs150
-rw-r--r--embassy-executor/src/arch/wasm.rs134
-rw-r--r--embassy-executor/src/arch/xtensa.rs135
-rw-r--r--embassy-executor/src/lib.rs83
-rw-r--r--embassy-executor/src/raw/mod.rs97
-rw-r--r--embassy-macros/src/macros/cortex_m_interrupt_take.rs4
-rw-r--r--embassy-stm32/build.rs17
-rw-r--r--embassy-stm32/src/rcc/f4.rs166
-rw-r--r--embassy-stm32/src/rcc/l4.rs133
-rw-r--r--embassy-stm32/src/usart/buffered.rs696
-rw-r--r--embassy-stm32/src/usart/mod.rs9
-rw-r--r--examples/boot/application/nrf/Cargo.toml2
-rw-r--r--examples/boot/application/rp/Cargo.toml2
-rw-r--r--examples/boot/application/stm32f3/Cargo.toml2
-rw-r--r--examples/boot/application/stm32f7/Cargo.toml2
-rw-r--r--examples/boot/application/stm32h7/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l0/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l1/Cargo.toml2
-rw-r--r--examples/boot/application/stm32l4/Cargo.toml2
-rw-r--r--examples/boot/application/stm32wl/Cargo.toml2
-rw-r--r--examples/nrf-rtos-trace/Cargo.toml2
-rw-r--r--examples/nrf52840/Cargo.toml2
-rw-r--r--examples/nrf5340/Cargo.toml2
-rw-r--r--examples/rp/Cargo.toml2
-rw-r--r--examples/std/Cargo.toml2
-rw-r--r--examples/stm32c0/Cargo.toml2
-rw-r--r--examples/stm32f0/Cargo.toml2
-rw-r--r--examples/stm32f0/src/bin/multiprio.rs2
-rw-r--r--examples/stm32f1/Cargo.toml2
-rw-r--r--examples/stm32f2/Cargo.toml2
-rw-r--r--examples/stm32f3/Cargo.toml2
-rw-r--r--examples/stm32f3/src/bin/multiprio.rs2
-rw-r--r--examples/stm32f4/Cargo.toml2
-rw-r--r--examples/stm32f4/src/bin/mco.rs30
-rw-r--r--examples/stm32f4/src/bin/multiprio.rs2
-rw-r--r--examples/stm32f4/src/bin/usart_buffered.rs14
-rw-r--r--examples/stm32f7/Cargo.toml2
-rw-r--r--examples/stm32g0/Cargo.toml2
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32h7/Cargo.toml2
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/src/bin/usart_irq.rs16
-rw-r--r--examples/stm32l1/Cargo.toml2
-rw-r--r--examples/stm32l4/Cargo.toml2
-rw-r--r--examples/stm32l4/src/bin/mco.rs27
-rw-r--r--examples/stm32l5/Cargo.toml2
-rw-r--r--examples/stm32u5/Cargo.toml2
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--examples/stm32wl/Cargo.toml2
-rw-r--r--examples/wasm/Cargo.toml2
-rw-r--r--tests/nrf/Cargo.toml2
-rw-r--r--tests/rp/Cargo.toml2
-rw-r--r--tests/stm32/Cargo.toml2
65 files changed, 1741 insertions, 996 deletions
diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml
index d9f8a285a..e3e446e63 100644
--- a/docs/modules/ROOT/examples/basic/Cargo.toml
+++ b/docs/modules/ROOT/examples/basic/Cargo.toml
@@ -6,7 +6,7 @@ version = "0.1.0"
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7 7
8[dependencies] 8[dependencies]
9embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
11embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } 11embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
12 12
diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml
index c9a963d4d..a11a7e0ba 100644
--- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml
+++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8cortex-m = "0.7" 8cortex-m = "0.7"
9cortex-m-rt = "0.7" 9cortex-m-rt = "0.7"
10embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } 10embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false }
11embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] } 11embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] }
12 12
13defmt = "0.3.0" 13defmt = "0.3.0"
14defmt-rtt = "0.3.0" 14defmt-rtt = "0.3.0"
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
index e2e361e3c..9d047f778 100644
--- a/embassy-boot/boot/src/boot_loader.rs
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -31,7 +31,7 @@ where
31} 31}
32 32
33/// Extension of the embedded-storage flash type information with block size and erase value. 33/// Extension of the embedded-storage flash type information with block size and erase value.
34pub trait Flash: NorFlash + ReadNorFlash { 34pub trait Flash: NorFlash {
35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase 35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
36 /// size of the flash, but for external QSPI flash modules, this can be lower. 36 /// size of the flash, but for external QSPI flash modules, this can be lower.
37 const BLOCK_SIZE: usize; 37 const BLOCK_SIZE: usize;
@@ -60,9 +60,11 @@ pub trait FlashConfig {
60/// different page sizes and flash write sizes. 60/// different page sizes and flash write sizes.
61pub struct BootLoader { 61pub struct BootLoader {
62 // Page with current state of bootloader. The state partition has the following format: 62 // Page with current state of bootloader. The state partition has the following format:
63 // | Range | Description | 63 // All ranges are in multiples of WRITE_SIZE bytes.
64 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 64 // | Range | Description |
65 // | WRITE_SIZE - N | Progress index used while swapping or reverting | 65 // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
66 // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
67 // | 2..2 + N | Progress index used while swapping or reverting |
66 state: Partition, 68 state: Partition,
67 // Location of the partition which will be booted from 69 // Location of the partition which will be booted from
68 active: Partition, 70 active: Partition,
@@ -192,12 +194,17 @@ impl BootLoader {
192 trace!("Reverting"); 194 trace!("Reverting");
193 self.revert(p, magic, page)?; 195 self.revert(p, magic, page)?;
194 196
195 // Overwrite magic and reset progress
196 let state_flash = p.state(); 197 let state_flash = p.state();
198
199 // Invalidate progress
197 magic.fill(!P::STATE::ERASE_VALUE); 200 magic.fill(!P::STATE::ERASE_VALUE);
198 self.state.write_blocking(state_flash, 0, magic)?; 201 self.state
202 .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?;
203
204 // Clear magic and progress
199 self.state.wipe_blocking(state_flash)?; 205 self.state.wipe_blocking(state_flash)?;
200 206
207 // Set magic
201 magic.fill(BOOT_MAGIC); 208 magic.fill(BOOT_MAGIC);
202 self.state.write_blocking(state_flash, 0, magic)?; 209 self.state.write_blocking(state_flash, 0, magic)?;
203 } 210 }
@@ -215,28 +222,34 @@ impl BootLoader {
215 222
216 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> { 223 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
217 let write_size = aligned.len(); 224 let write_size = aligned.len();
218 let max_index = ((self.state.len() - write_size) / write_size) - 1; 225 let max_index = ((self.state.len() - write_size) / write_size) - 2;
219 aligned.fill(!P::STATE::ERASE_VALUE); 226 aligned.fill(!P::STATE::ERASE_VALUE);
220 227
221 let state_flash = config.state(); 228 let state_flash = config.state();
222 for i in 0..max_index { 229
230 self.state
231 .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?;
232 if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) {
233 // Progress is invalid
234 return Ok(max_index);
235 }
236
237 for index in 0..max_index {
223 self.state 238 self.state
224 .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; 239 .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
225 240
226 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { 241 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
227 return Ok(i); 242 return Ok(index);
228 } 243 }
229 } 244 }
230 Ok(max_index) 245 Ok(max_index)
231 } 246 }
232 247
233 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { 248 fn update_progress<P: FlashConfig>(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
234 let write_size = magic.len();
235
236 let aligned = magic; 249 let aligned = magic;
237 aligned.fill(!P::STATE::ERASE_VALUE); 250 aligned.fill(!P::STATE::ERASE_VALUE);
238 self.state 251 self.state
239 .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; 252 .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
240 Ok(()) 253 Ok(())
241 } 254 }
242 255
@@ -360,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s
360 assert_eq!(active.len() % page_size, 0); 373 assert_eq!(active.len() % page_size, 0);
361 assert_eq!(dfu.len() % page_size, 0); 374 assert_eq!(dfu.len() % page_size, 0);
362 assert!(dfu.len() - active.len() >= page_size); 375 assert!(dfu.len() - active.len() >= page_size);
363 assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); 376 assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size);
364} 377}
365 378
366/// A flash wrapper implementing the Flash and embedded_storage traits. 379/// A flash wrapper implementing the Flash and embedded_storage traits.
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs
index 2d1b26980..819e20201 100644
--- a/embassy-boot/boot/src/firmware_updater.rs
+++ b/embassy-boot/boot/src/firmware_updater.rs
@@ -234,11 +234,24 @@ impl FirmwareUpdater {
234 self.state.read(state_flash, 0, aligned).await?; 234 self.state.read(state_flash, 0, aligned).await?;
235 235
236 if aligned.iter().any(|&b| b != magic) { 236 if aligned.iter().any(|&b| b != magic) {
237 aligned.fill(0); 237 // Read progress validity
238 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
239
240 // FIXME: Do not make this assumption.
241 const STATE_ERASE_VALUE: u8 = 0xFF;
242
243 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
244 // The current progress validity marker is invalid
245 } else {
246 // Invalidate progress
247 aligned.fill(!STATE_ERASE_VALUE);
248 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
249 }
238 250
239 self.state.write(state_flash, 0, aligned).await?; 251 // Clear magic and progress
240 self.state.wipe(state_flash).await?; 252 self.state.wipe(state_flash).await?;
241 253
254 // Set magic
242 aligned.fill(magic); 255 aligned.fill(magic);
243 self.state.write(state_flash, 0, aligned).await?; 256 self.state.write(state_flash, 0, aligned).await?;
244 } 257 }
@@ -441,11 +454,24 @@ impl FirmwareUpdater {
441 self.state.read_blocking(state_flash, 0, aligned)?; 454 self.state.read_blocking(state_flash, 0, aligned)?;
442 455
443 if aligned.iter().any(|&b| b != magic) { 456 if aligned.iter().any(|&b| b != magic) {
444 aligned.fill(0); 457 // Read progress validity
458 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
459
460 // FIXME: Do not make this assumption.
461 const STATE_ERASE_VALUE: u8 = 0xFF;
462
463 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
464 // The current progress validity marker is invalid
465 } else {
466 // Invalidate progress
467 aligned.fill(!STATE_ERASE_VALUE);
468 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
469 }
445 470
446 self.state.write_blocking(state_flash, 0, aligned)?; 471 // Clear magic and progress
447 self.state.wipe_blocking(state_flash)?; 472 self.state.wipe_blocking(state_flash)?;
448 473
474 // Set magic
449 aligned.fill(magic); 475 aligned.fill(magic);
450 self.state.write_blocking(state_flash, 0, aligned)?; 476 self.state.write_blocking(state_flash, 0, aligned)?;
451 } 477 }
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index da9055476..d4078f1cb 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -8,6 +8,7 @@ mod fmt;
8mod boot_loader; 8mod boot_loader;
9mod digest_adapters; 9mod digest_adapters;
10mod firmware_updater; 10mod firmware_updater;
11mod mem_flash;
11mod partition; 12mod partition;
12 13
13pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; 14pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig};
@@ -45,13 +46,10 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
45 46
46#[cfg(test)] 47#[cfg(test)]
47mod tests { 48mod tests {
48 use core::convert::Infallible;
49
50 use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
51 use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
52 use futures::executor::block_on; 49 use futures::executor::block_on;
53 50
54 use super::*; 51 use super::*;
52 use crate::mem_flash::MemFlash;
55 53
56 /* 54 /*
57 #[test] 55 #[test]
@@ -74,8 +72,8 @@ mod tests {
74 const ACTIVE: Partition = Partition::new(4096, 61440); 72 const ACTIVE: Partition = Partition::new(4096, 61440);
75 const DFU: Partition = Partition::new(61440, 122880); 73 const DFU: Partition = Partition::new(61440, 122880);
76 74
77 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 75 let mut flash = MemFlash::<131072, 4096, 4>::default();
78 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 76 flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
79 let mut flash = SingleFlashConfig::new(&mut flash); 77 let mut flash = SingleFlashConfig::new(&mut flash);
80 78
81 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 79 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -94,14 +92,14 @@ mod tests {
94 const STATE: Partition = Partition::new(0, 4096); 92 const STATE: Partition = Partition::new(0, 4096);
95 const ACTIVE: Partition = Partition::new(4096, 61440); 93 const ACTIVE: Partition = Partition::new(4096, 61440);
96 const DFU: Partition = Partition::new(61440, 122880); 94 const DFU: Partition = Partition::new(61440, 122880);
97 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 95 let mut flash = MemFlash::<131072, 4096, 4>::random();
98 96
99 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 97 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
100 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 98 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
101 let mut aligned = [0; 4]; 99 let mut aligned = [0; 4];
102 100
103 for i in ACTIVE.from..ACTIVE.to { 101 for i in ACTIVE.from..ACTIVE.to {
104 flash.0[i] = original[i - ACTIVE.from]; 102 flash.mem[i] = original[i - ACTIVE.from];
105 } 103 }
106 104
107 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 105 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -123,12 +121,12 @@ mod tests {
123 ); 121 );
124 122
125 for i in ACTIVE.from..ACTIVE.to { 123 for i in ACTIVE.from..ACTIVE.to {
126 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); 124 assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i);
127 } 125 }
128 126
129 // First DFU page is untouched 127 // First DFU page is untouched
130 for i in DFU.from + 4096..DFU.to { 128 for i in DFU.from + 4096..DFU.to {
131 assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i); 129 assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i);
132 } 130 }
133 131
134 // Running again should cause a revert 132 // Running again should cause a revert
@@ -140,12 +138,12 @@ mod tests {
140 ); 138 );
141 139
142 for i in ACTIVE.from..ACTIVE.to { 140 for i in ACTIVE.from..ACTIVE.to {
143 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); 141 assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i);
144 } 142 }
145 143
146 // Last page is untouched 144 // Last page is untouched
147 for i in DFU.from..DFU.to - 4096 { 145 for i in DFU.from..DFU.to - 4096 {
148 assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i); 146 assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i);
149 } 147 }
150 148
151 // Mark as booted 149 // Mark as booted
@@ -165,16 +163,16 @@ mod tests {
165 const ACTIVE: Partition = Partition::new(4096, 16384); 163 const ACTIVE: Partition = Partition::new(4096, 16384);
166 const DFU: Partition = Partition::new(0, 16384); 164 const DFU: Partition = Partition::new(0, 16384);
167 165
168 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); 166 let mut active = MemFlash::<16384, 4096, 8>::random();
169 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); 167 let mut dfu = MemFlash::<16384, 2048, 8>::random();
170 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 168 let mut state = MemFlash::<4096, 128, 4>::random();
171 let mut aligned = [0; 4]; 169 let mut aligned = [0; 4];
172 170
173 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 171 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
174 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 172 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
175 173
176 for i in ACTIVE.from..ACTIVE.to { 174 for i in ACTIVE.from..ACTIVE.to {
177 active.0[i] = original[i - ACTIVE.from]; 175 active.mem[i] = original[i - ACTIVE.from];
178 } 176 }
179 177
180 let mut updater = FirmwareUpdater::new(DFU, STATE); 178 let mut updater = FirmwareUpdater::new(DFU, STATE);
@@ -202,12 +200,12 @@ mod tests {
202 ); 200 );
203 201
204 for i in ACTIVE.from..ACTIVE.to { 202 for i in ACTIVE.from..ACTIVE.to {
205 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); 203 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
206 } 204 }
207 205
208 // First DFU page is untouched 206 // First DFU page is untouched
209 for i in DFU.from + 4096..DFU.to { 207 for i in DFU.from + 4096..DFU.to {
210 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); 208 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
211 } 209 }
212 } 210 }
213 211
@@ -219,15 +217,15 @@ mod tests {
219 const DFU: Partition = Partition::new(0, 16384); 217 const DFU: Partition = Partition::new(0, 16384);
220 218
221 let mut aligned = [0; 4]; 219 let mut aligned = [0; 4];
222 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); 220 let mut active = MemFlash::<16384, 2048, 4>::random();
223 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); 221 let mut dfu = MemFlash::<16384, 4096, 8>::random();
224 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 222 let mut state = MemFlash::<4096, 128, 4>::random();
225 223
226 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 224 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
227 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 225 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
228 226
229 for i in ACTIVE.from..ACTIVE.to { 227 for i in ACTIVE.from..ACTIVE.to {
230 active.0[i] = original[i - ACTIVE.from]; 228 active.mem[i] = original[i - ACTIVE.from];
231 } 229 }
232 230
233 let mut updater = FirmwareUpdater::new(DFU, STATE); 231 let mut updater = FirmwareUpdater::new(DFU, STATE);
@@ -254,12 +252,12 @@ mod tests {
254 ); 252 );
255 253
256 for i in ACTIVE.from..ACTIVE.to { 254 for i in ACTIVE.from..ACTIVE.to {
257 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); 255 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
258 } 256 }
259 257
260 // First DFU page is untouched 258 // First DFU page is untouched
261 for i in DFU.from + 4096..DFU.to { 259 for i in DFU.from + 4096..DFU.to {
262 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); 260 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
263 } 261 }
264 } 262 }
265 263
@@ -289,13 +287,13 @@ mod tests {
289 287
290 const STATE: Partition = Partition::new(0, 4096); 288 const STATE: Partition = Partition::new(0, 4096);
291 const DFU: Partition = Partition::new(4096, 8192); 289 const DFU: Partition = Partition::new(4096, 8192);
292 let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); 290 let mut flash = MemFlash::<8192, 4096, 4>::default();
293 291
294 let firmware_len = firmware.len(); 292 let firmware_len = firmware.len();
295 293
296 let mut write_buf = [0; 4096]; 294 let mut write_buf = [0; 4096];
297 write_buf[0..firmware_len].copy_from_slice(firmware); 295 write_buf[0..firmware_len].copy_from_slice(firmware);
298 NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); 296 DFU.write_blocking(&mut flash, 0, &write_buf).unwrap();
299 297
300 // On with the test 298 // On with the test
301 299
@@ -312,113 +310,4 @@ mod tests {
312 )) 310 ))
313 .is_ok()); 311 .is_ok());
314 } 312 }
315
316 pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>(pub [u8; SIZE]);
317
318 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
319 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
320 {
321 const WRITE_SIZE: usize = WRITE_SIZE;
322 const ERASE_SIZE: usize = ERASE_SIZE;
323 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
324 let from = from as usize;
325 let to = to as usize;
326 assert!(from % ERASE_SIZE == 0);
327 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
328 for i in from..to {
329 self.0[i] = 0xFF;
330 }
331 Ok(())
332 }
333
334 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
335 assert!(data.len() % WRITE_SIZE == 0);
336 assert!(offset as usize % WRITE_SIZE == 0);
337 assert!(offset as usize + data.len() <= SIZE);
338
339 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
340
341 Ok(())
342 }
343 }
344
345 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
346 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
347 {
348 type Error = Infallible;
349 }
350
351 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
352 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
353 {
354 const READ_SIZE: usize = 1;
355
356 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
357 let len = buf.len();
358 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
359 Ok(())
360 }
361
362 fn capacity(&self) -> usize {
363 SIZE
364 }
365 }
366
367 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
368 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
369 {
370 const BLOCK_SIZE: usize = ERASE_SIZE;
371 const ERASE_VALUE: u8 = 0xFF;
372 }
373
374 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
375 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
376 {
377 const READ_SIZE: usize = 1;
378
379 async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
380 let len = buf.len();
381 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
382 Ok(())
383 }
384
385 fn capacity(&self) -> usize {
386 SIZE
387 }
388 }
389
390 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
391 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
392 {
393 const WRITE_SIZE: usize = WRITE_SIZE;
394 const ERASE_SIZE: usize = ERASE_SIZE;
395
396 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
397 let from = from as usize;
398 let to = to as usize;
399 assert!(from % ERASE_SIZE == 0);
400 assert!(to % ERASE_SIZE == 0);
401 for i in from..to {
402 self.0[i] = 0xFF;
403 }
404 Ok(())
405 }
406
407 async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
408 info!("Writing {} bytes to 0x{:x}", data.len(), offset);
409 assert!(data.len() % WRITE_SIZE == 0);
410 assert!(offset as usize % WRITE_SIZE == 0);
411 assert!(
412 offset as usize + data.len() <= SIZE,
413 "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
414 offset,
415 data.len(),
416 SIZE
417 );
418
419 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
420
421 Ok(())
422 }
423 }
424} 313}
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs
new file mode 100644
index 000000000..828aad9d9
--- /dev/null
+++ b/embassy-boot/boot/src/mem_flash.rs
@@ -0,0 +1,156 @@
1#![allow(unused)]
2
3use core::ops::{Bound, Range, RangeBounds};
4
5use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
6use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
7
8use crate::Flash;
9
10pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
11 pub mem: [u8; SIZE],
12 pub pending_write_successes: Option<usize>,
13}
14
15#[derive(Debug)]
16pub struct MemFlashError;
17
18impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
19 pub const fn new(fill: u8) -> Self {
20 Self {
21 mem: [fill; SIZE],
22 pending_write_successes: None,
23 }
24 }
25
26 #[cfg(test)]
27 pub fn random() -> Self {
28 let mut mem = [0; SIZE];
29 for byte in mem.iter_mut() {
30 *byte = rand::random::<u8>();
31 }
32 Self {
33 mem,
34 pending_write_successes: None,
35 }
36 }
37}
38
39impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default
40 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
41{
42 fn default() -> Self {
43 Self::new(0xFF)
44 }
45}
46
47impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash
48 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
49{
50 const BLOCK_SIZE: usize = ERASE_SIZE;
51 const ERASE_VALUE: u8 = 0xFF;
52}
53
54impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
55 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
56{
57 type Error = MemFlashError;
58}
59
60impl NorFlashError for MemFlashError {
61 fn kind(&self) -> NorFlashErrorKind {
62 NorFlashErrorKind::Other
63 }
64}
65
66impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
67 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
68{
69 const READ_SIZE: usize = 1;
70
71 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
72 let len = bytes.len();
73 bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
74 Ok(())
75 }
76
77 fn capacity(&self) -> usize {
78 SIZE
79 }
80}
81
82impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
83 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
84{
85 const WRITE_SIZE: usize = WRITE_SIZE;
86 const ERASE_SIZE: usize = ERASE_SIZE;
87
88 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
89 let from = from as usize;
90 let to = to as usize;
91 assert!(from % ERASE_SIZE == 0);
92 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
93 for i in from..to {
94 self.mem[i] = 0xFF;
95 }
96 Ok(())
97 }
98
99 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
100 let offset = offset as usize;
101 assert!(bytes.len() % WRITE_SIZE == 0);
102 assert!(offset % WRITE_SIZE == 0);
103 assert!(offset + bytes.len() <= SIZE);
104
105 if let Some(pending_successes) = self.pending_write_successes {
106 if pending_successes > 0 {
107 self.pending_write_successes = Some(pending_successes - 1);
108 } else {
109 return Err(MemFlashError);
110 }
111 }
112
113 for ((offset, mem_byte), new_byte) in self
114 .mem
115 .iter_mut()
116 .enumerate()
117 .skip(offset)
118 .take(bytes.len())
119 .zip(bytes)
120 {
121 assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
122 *mem_byte = *new_byte;
123 }
124
125 Ok(())
126 }
127}
128
129impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
130 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
131{
132 const READ_SIZE: usize = 1;
133
134 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
135 <Self as ReadNorFlash>::read(self, offset, bytes)
136 }
137
138 fn capacity(&self) -> usize {
139 <Self as ReadNorFlash>::capacity(self)
140 }
141}
142
143impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
144 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
145{
146 const WRITE_SIZE: usize = WRITE_SIZE;
147 const ERASE_SIZE: usize = ERASE_SIZE;
148
149 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
150 <Self as NorFlash>::erase(self, from, to)
151 }
152
153 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
154 <Self as NorFlash>::write(self, offset, bytes)
155 }
156}
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs
index 3ccd4dd76..ac6b0ed0f 100644
--- a/embassy-boot/boot/src/partition.rs
+++ b/embassy-boot/boot/src/partition.rs
@@ -24,7 +24,7 @@ impl Partition {
24 } 24 }
25 25
26 /// Read from the partition on the provided flash 26 /// Read from the partition on the provided flash
27 pub(crate) async fn read<F: AsyncReadNorFlash>( 27 pub async fn read<F: AsyncReadNorFlash>(
28 &self, 28 &self,
29 flash: &mut F, 29 flash: &mut F,
30 offset: u32, 30 offset: u32,
@@ -35,12 +35,7 @@ impl Partition {
35 } 35 }
36 36
37 /// Write to the partition on the provided flash 37 /// Write to the partition on the provided flash
38 pub(crate) async fn write<F: AsyncNorFlash>( 38 pub async fn write<F: AsyncNorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
39 &self,
40 flash: &mut F,
41 offset: u32,
42 bytes: &[u8],
43 ) -> Result<(), F::Error> {
44 let offset = self.from as u32 + offset; 39 let offset = self.from as u32 + offset;
45 flash.write(offset, bytes).await?; 40 flash.write(offset, bytes).await?;
46 trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); 41 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
@@ -48,7 +43,7 @@ impl Partition {
48 } 43 }
49 44
50 /// Erase part of the partition on the provided flash 45 /// Erase part of the partition on the provided flash
51 pub(crate) async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { 46 pub async fn erase<F: AsyncNorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
52 let from = self.from as u32 + from; 47 let from = self.from as u32 + from;
53 let to = self.from as u32 + to; 48 let to = self.from as u32 + to;
54 flash.erase(from, to).await?; 49 flash.erase(from, to).await?;
@@ -66,18 +61,13 @@ impl Partition {
66 } 61 }
67 62
68 /// Read from the partition on the provided flash 63 /// Read from the partition on the provided flash
69 pub(crate) fn read_blocking<F: ReadNorFlash>( 64 pub fn read_blocking<F: ReadNorFlash>(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> {
70 &self,
71 flash: &mut F,
72 offset: u32,
73 bytes: &mut [u8],
74 ) -> Result<(), F::Error> {
75 let offset = self.from as u32 + offset; 65 let offset = self.from as u32 + offset;
76 flash.read(offset, bytes) 66 flash.read(offset, bytes)
77 } 67 }
78 68
79 /// Write to the partition on the provided flash 69 /// Write to the partition on the provided flash
80 pub(crate) fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { 70 pub fn write_blocking<F: NorFlash>(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> {
81 let offset = self.from as u32 + offset; 71 let offset = self.from as u32 + offset;
82 flash.write(offset, bytes)?; 72 flash.write(offset, bytes)?;
83 trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); 73 trace!("Wrote from 0x{:x} len {}", offset, bytes.len());
@@ -85,7 +75,7 @@ impl Partition {
85 } 75 }
86 76
87 /// Erase part of the partition on the provided flash 77 /// Erase part of the partition on the provided flash
88 pub(crate) fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { 78 pub fn erase_blocking<F: NorFlash>(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> {
89 let from = self.from as u32 + from; 79 let from = self.from as u32 + from;
90 let to = self.from as u32 + to; 80 let to = self.from as u32 + to;
91 flash.erase(from, to)?; 81 flash.erase(from, to)?;
@@ -105,45 +95,45 @@ impl Partition {
105 95
106#[cfg(test)] 96#[cfg(test)]
107mod tests { 97mod tests {
108 use crate::tests::MemFlash; 98 use crate::mem_flash::MemFlash;
109 use crate::Partition; 99 use crate::Partition;
110 100
111 #[test] 101 #[test]
112 fn can_erase() { 102 fn can_erase() {
113 let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); 103 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
114 let partition = Partition::new(256, 512); 104 let partition = Partition::new(256, 512);
115 105
116 partition.erase_blocking(&mut flash, 64, 192).unwrap(); 106 partition.erase_blocking(&mut flash, 64, 192).unwrap();
117 107
118 for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { 108 for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) {
119 assert_eq!(0x00, byte, "Index {}", index); 109 assert_eq!(0x00, byte, "Index {}", index);
120 } 110 }
121 111
122 for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { 112 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) {
123 assert_eq!(0xFF, byte, "Index {}", index); 113 assert_eq!(0xFF, byte, "Index {}", index);
124 } 114 }
125 115
126 for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { 116 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) {
127 assert_eq!(0x00, byte, "Index {}", index); 117 assert_eq!(0x00, byte, "Index {}", index);
128 } 118 }
129 } 119 }
130 120
131 #[test] 121 #[test]
132 fn can_wipe() { 122 fn can_wipe() {
133 let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); 123 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
134 let partition = Partition::new(256, 512); 124 let partition = Partition::new(256, 512);
135 125
136 partition.wipe_blocking(&mut flash).unwrap(); 126 partition.wipe_blocking(&mut flash).unwrap();
137 127
138 for (index, byte) in flash.0.iter().copied().enumerate().take(256) { 128 for (index, byte) in flash.mem.iter().copied().enumerate().take(256) {
139 assert_eq!(0x00, byte, "Index {}", index); 129 assert_eq!(0x00, byte, "Index {}", index);
140 } 130 }
141 131
142 for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { 132 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) {
143 assert_eq!(0xFF, byte, "Index {}", index); 133 assert_eq!(0xFF, byte, "Index {}", index);
144 } 134 }
145 135
146 for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { 136 for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) {
147 assert_eq!(0x00, byte, "Index {}", index); 137 assert_eq!(0x00, byte, "Index {}", index);
148 } 138 }
149 } 139 }
diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs
deleted file mode 100644
index 558539e73..000000000
--- a/embassy-cortex-m/src/executor.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1//! Executor specific to cortex-m devices.
2
3use core::cell::UnsafeCell;
4use core::mem::MaybeUninit;
5
6use atomic_polyfill::{AtomicBool, Ordering};
7use cortex_m::interrupt::InterruptNumber;
8use cortex_m::peripheral::NVIC;
9pub use embassy_executor::*;
10
11#[derive(Clone, Copy)]
12struct N(u16);
13unsafe impl cortex_m::interrupt::InterruptNumber for N {
14 fn number(self) -> u16 {
15 self.0
16 }
17}
18
19fn pend_by_number(n: u16) {
20 cortex_m::peripheral::NVIC::pend(N(n))
21}
22
23/// Interrupt mode executor.
24///
25/// This executor runs tasks in interrupt mode. The interrupt handler is set up
26/// to poll tasks, and when a task is woken the interrupt is pended from software.
27///
28/// This allows running async tasks at a priority higher than thread mode. One
29/// use case is to leave thread mode free for non-async tasks. Another use case is
30/// to run multiple executors: one in thread mode for low priority tasks and another in
31/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
32/// priority ones.
33///
34/// It is even possible to run multiple interrupt mode executors at different priorities,
35/// by assigning different priorities to the interrupts. For an example on how to do this,
36/// See the 'multiprio' example for 'embassy-nrf'.
37///
38/// To use it, you have to pick an interrupt that won't be used by the hardware.
39/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
40/// If this is not the case, you may use an interrupt from any unused peripheral.
41///
42/// It is somewhat more complex to use, it's recommended to use the thread-mode
43/// [`Executor`] instead, if it works for your use case.
44pub struct InterruptExecutor {
45 started: AtomicBool,
46 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
47}
48
49unsafe impl Send for InterruptExecutor {}
50unsafe impl Sync for InterruptExecutor {}
51
52impl InterruptExecutor {
53 /// Create a new, not started `InterruptExecutor`.
54 #[inline]
55 pub const fn new() -> Self {
56 Self {
57 started: AtomicBool::new(false),
58 executor: UnsafeCell::new(MaybeUninit::uninit()),
59 }
60 }
61
62 /// Executor interrupt callback.
63 ///
64 /// # Safety
65 ///
66 /// You MUST call this from the interrupt handler, and from nowhere else.
67 pub unsafe fn on_interrupt(&'static self) {
68 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
69 executor.poll();
70 }
71
72 /// Start the executor.
73 ///
74 /// This initializes the executor, enables the interrupt, and returns.
75 /// The executor keeps running in the background through the interrupt.
76 ///
77 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
78 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
79 /// different "thread" (the interrupt), so spawning tasks on it is effectively
80 /// sending them.
81 ///
82 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
83 /// a task running in it.
84 ///
85 /// # Interrupt requirements
86 ///
87 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
88 ///
89 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
90 ///
91 /// You must set the interrupt priority before calling this method. You MUST NOT
92 /// do it after.
93 ///
94 pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner {
95 if self
96 .started
97 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
98 .is_err()
99 {
100 panic!("InterruptExecutor::start() called multiple times on the same executor.");
101 }
102
103 unsafe {
104 (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new(
105 |ctx| pend_by_number(ctx as u16),
106 irq.number() as *mut (),
107 ))
108 }
109
110 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
111
112 unsafe { NVIC::unmask(irq) }
113
114 executor.spawner().make_send()
115 }
116}
diff --git a/embassy-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs
index fba23367b..e4b713a06 100644
--- a/embassy-cortex-m/src/lib.rs
+++ b/embassy-cortex-m/src/lib.rs
@@ -5,6 +5,6 @@
5// This mod MUST go first, so that the others see its macros. 5// This mod MUST go first, so that the others see its macros.
6pub(crate) mod fmt; 6pub(crate) mod fmt;
7 7
8pub mod executor; 8pub use embassy_executor as executor;
9pub mod interrupt; 9pub mod interrupt;
10pub mod peripheral; 10pub mod peripheral;
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index 8ad3fd698..bb8a46c82 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -31,9 +31,22 @@ flavors = [
31features = ["std", "nightly", "defmt"] 31features = ["std", "nightly", "defmt"]
32 32
33[features] 33[features]
34default = [] 34
35std = ["critical-section/std"] 35# Architecture
36wasm = ["dep:wasm-bindgen", "dep:js-sys"] 36_arch = [] # some arch was picked
37arch-std = ["_arch", "critical-section/std"]
38arch-cortex-m = ["_arch", "dep:cortex-m"]
39arch-xtensa = ["_arch"]
40arch-riscv32 = ["_arch"]
41arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
42
43# Enable creating a `Pender` from an arbitrary function pointer callback.
44pender-callback = []
45
46# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
47executor-thread = []
48# Enable the interrupt-mode executor (available in Cortex-M only)
49executor-interrupt = []
37 50
38# Enable nightly-only features 51# Enable nightly-only features
39nightly = [] 52nightly = []
@@ -55,9 +68,11 @@ embassy-macros = { version = "0.1.0", path = "../embassy-macros" }
55embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} 68embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
56atomic-polyfill = "1.0.1" 69atomic-polyfill = "1.0.1"
57critical-section = "1.1" 70critical-section = "1.1"
58cfg-if = "1.0.0"
59static_cell = "1.0" 71static_cell = "1.0"
60 72
61# WASM dependencies 73# arch-cortex-m dependencies
74cortex-m = { version = "0.7.6", optional = true }
75
76# arch-wasm dependencies
62wasm-bindgen = { version = "0.2.82", optional = true } 77wasm-bindgen = { version = "0.2.82", optional = true }
63js-sys = { version = "0.3", optional = true } 78js-sys = { version = "0.3", optional = true }
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 4b27a264e..d6a55c4c7 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -1,59 +1,209 @@
1use core::arch::asm; 1#[cfg(feature = "executor-thread")]
2use core::marker::PhantomData; 2pub use thread::*;
3use core::ptr; 3#[cfg(feature = "executor-thread")]
4 4mod thread {
5use super::{raw, Spawner}; 5 use core::arch::asm;
6 6 use core::marker::PhantomData;
7/// Thread mode executor, using WFE/SEV. 7
8/// 8 #[cfg(feature = "nightly")]
9/// This is the simplest and most common kind of executor. It runs on 9 pub use embassy_macros::main_cortex_m as main;
10/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 10
11/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 11 use crate::raw::{Pender, PenderInner};
12/// is executed, to make the `WFE` exit from sleep and poll the task. 12 use crate::{raw, Spawner};
13/// 13
14/// This executor allows for ultra low power consumption for chips where `WFE` 14 #[derive(Copy, Clone)]
15/// triggers low-power sleep without extra steps. If your chip requires extra steps, 15 pub(crate) struct ThreadPender;
16/// you may use [`raw::Executor`] directly to program custom behavior. 16
17pub struct Executor { 17 impl ThreadPender {
18 inner: raw::Executor, 18 pub(crate) fn pend(self) {
19 not_send: PhantomData<*mut ()>, 19 unsafe { core::arch::asm!("sev") }
20 }
21 }
22
23 /// Thread mode executor, using WFE/SEV.
24 ///
25 /// This is the simplest and most common kind of executor. It runs on
26 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
27 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
28 /// is executed, to make the `WFE` exit from sleep and poll the task.
29 ///
30 /// This executor allows for ultra low power consumption for chips where `WFE`
31 /// triggers low-power sleep without extra steps. If your chip requires extra steps,
32 /// you may use [`raw::Executor`] directly to program custom behavior.
33 pub struct Executor {
34 inner: raw::Executor,
35 not_send: PhantomData<*mut ()>,
36 }
37
38 impl Executor {
39 /// Create a new Executor.
40 pub fn new() -> Self {
41 Self {
42 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
43 not_send: PhantomData,
44 }
45 }
46
47 /// Run the executor.
48 ///
49 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
50 /// this executor. Use it to spawn the initial task(s). After `init` returns,
51 /// the executor starts running the tasks.
52 ///
53 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
54 /// for example by passing it as an argument to the initial tasks.
55 ///
56 /// This function requires `&'static mut self`. This means you have to store the
57 /// Executor instance in a place where it'll live forever and grants you mutable
58 /// access. There's a few ways to do this:
59 ///
60 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
61 /// - a `static mut` (unsafe)
62 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
63 ///
64 /// This function never returns.
65 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
66 init(self.inner.spawner());
67
68 loop {
69 unsafe {
70 self.inner.poll();
71 asm!("wfe");
72 };
73 }
74 }
75 }
20} 76}
21 77
22impl Executor { 78#[cfg(feature = "executor-interrupt")]
23 /// Create a new Executor. 79pub use interrupt::*;
24 pub fn new() -> Self { 80#[cfg(feature = "executor-interrupt")]
25 Self { 81mod interrupt {
26 inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), 82 use core::cell::UnsafeCell;
27 not_send: PhantomData, 83 use core::mem::MaybeUninit;
84
85 use atomic_polyfill::{AtomicBool, Ordering};
86 use cortex_m::interrupt::InterruptNumber;
87 use cortex_m::peripheral::NVIC;
88
89 use crate::raw::{self, Pender, PenderInner};
90
91 #[derive(Clone, Copy)]
92 pub(crate) struct InterruptPender(u16);
93
94 impl InterruptPender {
95 pub(crate) fn pend(self) {
96 // STIR is faster, but is only available in v7 and higher.
97 #[cfg(not(armv6m))]
98 {
99 let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
100 nvic.request(self);
101 }
102
103 #[cfg(armv6m)]
104 cortex_m::peripheral::NVIC::pend(self);
28 } 105 }
29 } 106 }
30 107
31 /// Run the executor. 108 unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
109 fn number(self) -> u16 {
110 self.0
111 }
112 }
113
114 /// Interrupt mode executor.
32 /// 115 ///
33 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 116 /// This executor runs tasks in interrupt mode. The interrupt handler is set up
34 /// this executor. Use it to spawn the initial task(s). After `init` returns, 117 /// to poll tasks, and when a task is woken the interrupt is pended from software.
35 /// the executor starts running the tasks.
36 /// 118 ///
37 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 119 /// This allows running async tasks at a priority higher than thread mode. One
38 /// for example by passing it as an argument to the initial tasks. 120 /// use case is to leave thread mode free for non-async tasks. Another use case is
121 /// to run multiple executors: one in thread mode for low priority tasks and another in
122 /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
123 /// priority ones.
39 /// 124 ///
40 /// This function requires `&'static mut self`. This means you have to store the 125 /// It is even possible to run multiple interrupt mode executors at different priorities,
41 /// Executor instance in a place where it'll live forever and grants you mutable 126 /// by assigning different priorities to the interrupts. For an example on how to do this,
42 /// access. There's a few ways to do this: 127 /// See the 'multiprio' example for 'embassy-nrf'.
43 /// 128 ///
44 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 129 /// To use it, you have to pick an interrupt that won't be used by the hardware.
45 /// - a `static mut` (unsafe) 130 /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
46 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 131 /// If this is not the case, you may use an interrupt from any unused peripheral.
47 /// 132 ///
48 /// This function never returns. 133 /// It is somewhat more complex to use, it's recommended to use the thread-mode
49 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 134 /// [`Executor`] instead, if it works for your use case.
50 init(self.inner.spawner()); 135 pub struct InterruptExecutor {
136 started: AtomicBool,
137 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
138 }
139
140 unsafe impl Send for InterruptExecutor {}
141 unsafe impl Sync for InterruptExecutor {}
142
143 impl InterruptExecutor {
144 /// Create a new, not started `InterruptExecutor`.
145 #[inline]
146 pub const fn new() -> Self {
147 Self {
148 started: AtomicBool::new(false),
149 executor: UnsafeCell::new(MaybeUninit::uninit()),
150 }
151 }
152
153 /// Executor interrupt callback.
154 ///
155 /// # Safety
156 ///
157 /// You MUST call this from the interrupt handler, and from nowhere else.
158 pub unsafe fn on_interrupt(&'static self) {
159 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
160 executor.poll();
161 }
162
163 /// Start the executor.
164 ///
165 /// This initializes the executor, enables the interrupt, and returns.
166 /// The executor keeps running in the background through the interrupt.
167 ///
168 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
169 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
170 /// different "thread" (the interrupt), so spawning tasks on it is effectively
171 /// sending them.
172 ///
173 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
174 /// a task running in it.
175 ///
176 /// # Interrupt requirements
177 ///
178 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
179 ///
180 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
181 ///
182 /// You must set the interrupt priority before calling this method. You MUST NOT
183 /// do it after.
184 ///
185 pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
186 if self
187 .started
188 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
189 .is_err()
190 {
191 panic!("InterruptExecutor::start() called multiple times on the same executor.");
192 }
51 193
52 loop {
53 unsafe { 194 unsafe {
54 self.inner.poll(); 195 (&mut *self.executor.get())
55 asm!("wfe"); 196 .as_mut_ptr()
56 }; 197 .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
198 irq.number(),
199 )))))
200 }
201
202 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
203
204 unsafe { NVIC::unmask(irq) }
205
206 executor.spawner().make_send()
57 } 207 }
58 } 208 }
59} 209}
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs
index e97a56cda..f66daeae4 100644
--- a/embassy-executor/src/arch/riscv32.rs
+++ b/embassy-executor/src/arch/riscv32.rs
@@ -1,72 +1,83 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use core::ptr; 2compile_error!("`executor-interrupt` is not supported with `arch-riscv32`.");
3use core::sync::atomic::{AtomicBool, Ordering};
4 3
5use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering};
6 10
7/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV 11 use crate::raw::{Pender, PenderInner};
8/// 12 use crate::{raw, Spawner};
9static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
10 13
11/// RISCV32 Executor 14 #[derive(Copy, Clone)]
12pub struct Executor { 15 pub(crate) struct ThreadPender;
13 inner: raw::Executor,
14 not_send: PhantomData<*mut ()>,
15}
16 16
17impl Executor { 17 impl ThreadPender {
18 /// Create a new Executor. 18 #[allow(unused)]
19 pub fn new() -> Self { 19 pub(crate) fn pend(self) {
20 Self { 20 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
21 // use Signal_Work_Thread_Mode as substitute for local interrupt register
22 inner: raw::Executor::new(
23 |_| {
24 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
25 },
26 ptr::null_mut(),
27 ),
28 not_send: PhantomData,
29 } 21 }
30 } 22 }
31 23
32 /// Run the executor. 24 /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
33 /// 25 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
34 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 26
35 /// this executor. Use it to spawn the initial task(s). After `init` returns, 27 /// RISCV32 Executor
36 /// the executor starts running the tasks. 28 pub struct Executor {
37 /// 29 inner: raw::Executor,
38 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 30 not_send: PhantomData<*mut ()>,
39 /// for example by passing it as an argument to the initial tasks. 31 }
40 /// 32
41 /// This function requires `&'static mut self`. This means you have to store the 33 impl Executor {
42 /// Executor instance in a place where it'll live forever and grants you mutable 34 /// Create a new Executor.
43 /// access. There's a few ways to do this: 35 pub fn new() -> Self {
44 /// 36 Self {
45 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 37 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
46 /// - a `static mut` (unsafe) 38 not_send: PhantomData,
47 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 39 }
48 /// 40 }
49 /// This function never returns. 41
50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 42 /// Run the executor.
51 init(self.inner.spawner()); 43 ///
44 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
45 /// this executor. Use it to spawn the initial task(s). After `init` returns,
46 /// the executor starts running the tasks.
47 ///
48 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
49 /// for example by passing it as an argument to the initial tasks.
50 ///
51 /// This function requires `&'static mut self`. This means you have to store the
52 /// Executor instance in a place where it'll live forever and grants you mutable
53 /// access. There's a few ways to do this:
54 ///
55 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
56 /// - a `static mut` (unsafe)
57 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
58 ///
59 /// This function never returns.
60 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
61 init(self.inner.spawner());
52 62
53 loop { 63 loop {
54 unsafe { 64 unsafe {
55 self.inner.poll(); 65 self.inner.poll();
56 // we do not care about race conditions between the load and store operations, interrupts 66 // we do not care about race conditions between the load and store operations, interrupts
57 //will only set this value to true. 67 //will only set this value to true.
58 critical_section::with(|_| { 68 critical_section::with(|_| {
59 // if there is work to do, loop back to polling 69 // if there is work to do, loop back to polling
60 // TODO can we relax this? 70 // TODO can we relax this?
61 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 71 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
62 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 72 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
63 } 73 }
64 // if not, wait for interrupt 74 // if not, wait for interrupt
65 else { 75 else {
66 core::arch::asm!("wfi"); 76 core::arch::asm!("wfi");
67 } 77 }
68 }); 78 });
69 // if an interrupt occurred while waiting, it will be serviced here 79 // if an interrupt occurred while waiting, it will be serviced here
80 }
70 } 81 }
71 } 82 }
72 } 83 }
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index 701f0eb18..4e4a178f0 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -1,84 +1,100 @@
1use std::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use std::sync::{Condvar, Mutex}; 2compile_error!("`executor-interrupt` is not supported with `arch-std`.");
3 3
4use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use std::marker::PhantomData;
9 use std::sync::{Condvar, Mutex};
5 10
6/// Single-threaded std-based executor. 11 #[cfg(feature = "nightly")]
7pub struct Executor { 12 pub use embassy_macros::main_std as main;
8 inner: raw::Executor, 13
9 not_send: PhantomData<*mut ()>, 14 use crate::raw::{Pender, PenderInner};
10 signaler: &'static Signaler, 15 use crate::{raw, Spawner};
11}
12 16
13impl Executor { 17 #[derive(Copy, Clone)]
14 /// Create a new Executor. 18 pub(crate) struct ThreadPender(&'static Signaler);
15 pub fn new() -> Self { 19
16 let signaler = &*Box::leak(Box::new(Signaler::new())); 20 impl ThreadPender {
17 Self { 21 #[allow(unused)]
18 inner: raw::Executor::new( 22 pub(crate) fn pend(self) {
19 |p| unsafe { 23 self.0.signal()
20 let s = &*(p as *const () as *const Signaler);
21 s.signal()
22 },
23 signaler as *const _ as _,
24 ),
25 not_send: PhantomData,
26 signaler,
27 } 24 }
28 } 25 }
29 26
30 /// Run the executor. 27 /// Single-threaded std-based executor.
31 /// 28 pub struct Executor {
32 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 29 inner: raw::Executor,
33 /// this executor. Use it to spawn the initial task(s). After `init` returns, 30 not_send: PhantomData<*mut ()>,
34 /// the executor starts running the tasks. 31 signaler: &'static Signaler,
35 /// 32 }
36 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
37 /// for example by passing it as an argument to the initial tasks.
38 ///
39 /// This function requires `&'static mut self`. This means you have to store the
40 /// Executor instance in a place where it'll live forever and grants you mutable
41 /// access. There's a few ways to do this:
42 ///
43 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
44 /// - a `static mut` (unsafe)
45 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
46 ///
47 /// This function never returns.
48 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
49 init(self.inner.spawner());
50 33
51 loop { 34 impl Executor {
52 unsafe { self.inner.poll() }; 35 /// Create a new Executor.
53 self.signaler.wait() 36 pub fn new() -> Self {
37 let signaler = &*Box::leak(Box::new(Signaler::new()));
38 Self {
39 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
40 not_send: PhantomData,
41 signaler,
42 }
54 } 43 }
55 }
56}
57 44
58struct Signaler { 45 /// Run the executor.
59 mutex: Mutex<bool>, 46 ///
60 condvar: Condvar, 47 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
61} 48 /// this executor. Use it to spawn the initial task(s). After `init` returns,
49 /// the executor starts running the tasks.
50 ///
51 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
52 /// for example by passing it as an argument to the initial tasks.
53 ///
54 /// This function requires `&'static mut self`. This means you have to store the
55 /// Executor instance in a place where it'll live forever and grants you mutable
56 /// access. There's a few ways to do this:
57 ///
58 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
59 /// - a `static mut` (unsafe)
60 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
61 ///
62 /// This function never returns.
63 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
64 init(self.inner.spawner());
62 65
63impl Signaler { 66 loop {
64 fn new() -> Self { 67 unsafe { self.inner.poll() };
65 Self { 68 self.signaler.wait()
66 mutex: Mutex::new(false), 69 }
67 condvar: Condvar::new(),
68 } 70 }
69 } 71 }
70 72
71 fn wait(&self) { 73 struct Signaler {
72 let mut signaled = self.mutex.lock().unwrap(); 74 mutex: Mutex<bool>,
73 while !*signaled { 75 condvar: Condvar,
74 signaled = self.condvar.wait(signaled).unwrap();
75 }
76 *signaled = false;
77 } 76 }
78 77
79 fn signal(&self) { 78 impl Signaler {
80 let mut signaled = self.mutex.lock().unwrap(); 79 fn new() -> Self {
81 *signaled = true; 80 Self {
82 self.condvar.notify_one(); 81 mutex: Mutex::new(false),
82 condvar: Condvar::new(),
83 }
84 }
85
86 fn wait(&self) {
87 let mut signaled = self.mutex.lock().unwrap();
88 while !*signaled {
89 signaled = self.condvar.wait(signaled).unwrap();
90 }
91 *signaled = false;
92 }
93
94 fn signal(&self) {
95 let mut signaled = self.mutex.lock().unwrap();
96 *signaled = true;
97 self.condvar.notify_one();
98 }
83 } 99 }
84} 100}
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs
index 98091cfbb..08ab16b99 100644
--- a/embassy-executor/src/arch/wasm.rs
+++ b/embassy-executor/src/arch/wasm.rs
@@ -1,74 +1,88 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-wasm`.");
2 3
3use js_sys::Promise; 4#[cfg(feature = "executor-thread")]
4use wasm_bindgen::prelude::*; 5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
5 8
6use super::raw::util::UninitCell; 9 use core::marker::PhantomData;
7use super::raw::{self};
8use super::Spawner;
9 10
10/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. 11 #[cfg(feature = "nightly")]
11pub struct Executor { 12 pub use embassy_macros::main_wasm as main;
12 inner: raw::Executor, 13 use js_sys::Promise;
13 ctx: &'static WasmContext, 14 use wasm_bindgen::prelude::*;
14 not_send: PhantomData<*mut ()>,
15}
16 15
17pub(crate) struct WasmContext { 16 use crate::raw::util::UninitCell;
18 promise: Promise, 17 use crate::raw::{Pender, PenderInner};
19 closure: UninitCell<Closure<dyn FnMut(JsValue)>>, 18 use crate::{raw, Spawner};
20} 19
20 /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
21 pub struct Executor {
22 inner: raw::Executor,
23 ctx: &'static WasmContext,
24 not_send: PhantomData<*mut ()>,
25 }
26
27 pub(crate) struct WasmContext {
28 promise: Promise,
29 closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
30 }
31
32 #[derive(Copy, Clone)]
33 pub(crate) struct ThreadPender(&'static WasmContext);
21 34
22impl WasmContext { 35 impl ThreadPender {
23 pub fn new() -> Self { 36 #[allow(unused)]
24 Self { 37 pub(crate) fn pend(self) {
25 promise: Promise::resolve(&JsValue::undefined()), 38 let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
26 closure: UninitCell::uninit(),
27 } 39 }
28 } 40 }
29}
30 41
31impl Executor { 42 impl WasmContext {
32 /// Create a new Executor. 43 pub fn new() -> Self {
33 pub fn new() -> Self { 44 Self {
34 let ctx = &*Box::leak(Box::new(WasmContext::new())); 45 promise: Promise::resolve(&JsValue::undefined()),
35 let inner = raw::Executor::new( 46 closure: UninitCell::uninit(),
36 |p| unsafe { 47 }
37 let ctx = &*(p as *const () as *const WasmContext);
38 let _ = ctx.promise.then(ctx.closure.as_mut());
39 },
40 ctx as *const _ as _,
41 );
42 Self {
43 inner,
44 not_send: PhantomData,
45 ctx,
46 } 48 }
47 } 49 }
48 50
49 /// Run the executor. 51 impl Executor {
50 /// 52 /// Create a new Executor.
51 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 53 pub fn new() -> Self {
52 /// this executor. Use it to spawn the initial task(s). After `init` returns, 54 let ctx = &*Box::leak(Box::new(WasmContext::new()));
53 /// the executor starts running the tasks. 55 Self {
54 /// 56 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
55 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 57 not_send: PhantomData,
56 /// for example by passing it as an argument to the initial tasks. 58 ctx,
57 /// 59 }
58 /// This function requires `&'static mut self`. This means you have to store the 60 }
59 /// Executor instance in a place where it'll live forever and grants you mutable 61
60 /// access. There's a few ways to do this: 62 /// Run the executor.
61 /// 63 ///
62 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 64 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
63 /// - a `static mut` (unsafe) 65 /// this executor. Use it to spawn the initial task(s). After `init` returns,
64 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 66 /// the executor starts running the tasks.
65 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { 67 ///
66 unsafe { 68 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
67 let executor = &self.inner; 69 /// for example by passing it as an argument to the initial tasks.
68 self.ctx.closure.write(Closure::new(move |_| { 70 ///
69 executor.poll(); 71 /// This function requires `&'static mut self`. This means you have to store the
70 })); 72 /// Executor instance in a place where it'll live forever and grants you mutable
71 init(self.inner.spawner()); 73 /// access. There's a few ways to do this:
74 ///
75 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
76 /// - a `static mut` (unsafe)
77 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
78 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
79 unsafe {
80 let executor = &self.inner;
81 self.ctx.closure.write(Closure::new(move |_| {
82 executor.poll();
83 }));
84 init(self.inner.spawner());
85 }
72 } 86 }
73 } 87 }
74} 88}
diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs
index 4ee0d9f78..61ea92c16 100644
--- a/embassy-executor/src/arch/xtensa.rs
+++ b/embassy-executor/src/arch/xtensa.rs
@@ -1,73 +1,84 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use core::ptr; 2compile_error!("`executor-interrupt` is not supported with `arch-xtensa`.");
3use core::sync::atomic::{AtomicBool, Ordering};
4 3
5use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering};
6 10
7/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa 11 use crate::raw::{Pender, PenderInner};
8/// 12 use crate::{raw, Spawner};
9static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
10 13
11/// Xtensa Executor 14 #[derive(Copy, Clone)]
12pub struct Executor { 15 pub(crate) struct ThreadPender;
13 inner: raw::Executor,
14 not_send: PhantomData<*mut ()>,
15}
16 16
17impl Executor { 17 impl ThreadPender {
18 /// Create a new Executor. 18 #[allow(unused)]
19 pub fn new() -> Self { 19 pub(crate) fn pend(self) {
20 Self { 20 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
21 // use Signal_Work_Thread_Mode as substitute for local interrupt register
22 inner: raw::Executor::new(
23 |_| {
24 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
25 },
26 ptr::null_mut(),
27 ),
28 not_send: PhantomData,
29 } 21 }
30 } 22 }
31 23
32 /// Run the executor. 24 /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
33 /// 25 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
34 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 26
35 /// this executor. Use it to spawn the initial task(s). After `init` returns, 27 /// Xtensa Executor
36 /// the executor starts running the tasks. 28 pub struct Executor {
37 /// 29 inner: raw::Executor,
38 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 30 not_send: PhantomData<*mut ()>,
39 /// for example by passing it as an argument to the initial tasks. 31 }
40 /// 32
41 /// This function requires `&'static mut self`. This means you have to store the 33 impl Executor {
42 /// Executor instance in a place where it'll live forever and grants you mutable 34 /// Create a new Executor.
43 /// access. There's a few ways to do this: 35 pub fn new() -> Self {
44 /// 36 Self {
45 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 37 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
46 /// - a `static mut` (unsafe) 38 not_send: PhantomData,
47 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 39 }
48 /// 40 }
49 /// This function never returns. 41
50 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 42 /// Run the executor.
51 init(self.inner.spawner()); 43 ///
44 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
45 /// this executor. Use it to spawn the initial task(s). After `init` returns,
46 /// the executor starts running the tasks.
47 ///
48 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
49 /// for example by passing it as an argument to the initial tasks.
50 ///
51 /// This function requires `&'static mut self`. This means you have to store the
52 /// Executor instance in a place where it'll live forever and grants you mutable
53 /// access. There's a few ways to do this:
54 ///
55 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
56 /// - a `static mut` (unsafe)
57 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
58 ///
59 /// This function never returns.
60 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
61 init(self.inner.spawner());
52 62
53 loop { 63 loop {
54 unsafe { 64 unsafe {
55 self.inner.poll(); 65 self.inner.poll();
56 // we do not care about race conditions between the load and store operations, interrupts 66 // we do not care about race conditions between the load and store operations, interrupts
57 // will only set this value to true. 67 // will only set this value to true.
58 // if there is work to do, loop back to polling 68 // if there is work to do, loop back to polling
59 // TODO can we relax this? 69 // TODO can we relax this?
60 critical_section::with(|_| { 70 critical_section::with(|_| {
61 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 71 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
62 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 72 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
63 } else { 73 } else {
64 // waiti sets the PS.INTLEVEL when slipping into sleep 74 // waiti sets the PS.INTLEVEL when slipping into sleep
65 // because critical sections in Xtensa are implemented via increasing 75 // because critical sections in Xtensa are implemented via increasing
66 // PS.INTLEVEL the critical section ends here 76 // PS.INTLEVEL the critical section ends here
67 // take care not add code after `waiti` if it needs to be inside the CS 77 // take care not add code after `waiti` if it needs to be inside the CS
68 core::arch::asm!("waiti 0"); // critical section ends here 78 core::arch::asm!("waiti 0"); // critical section ends here
69 } 79 }
70 }); 80 });
81 }
71 } 82 }
72 } 83 }
73 } 84 }
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index 4c7e2f4cd..3ce687eb6 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -1,5 +1,5 @@
1#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] 1#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
2#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] 2#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))]
3#![allow(clippy::new_without_default)] 3#![allow(clippy::new_without_default)]
4#![doc = include_str!("../README.md")] 4#![doc = include_str!("../README.md")]
5#![warn(missing_docs)] 5#![warn(missing_docs)]
@@ -10,47 +10,43 @@ pub(crate) mod fmt;
10#[cfg(feature = "nightly")] 10#[cfg(feature = "nightly")]
11pub use embassy_macros::task; 11pub use embassy_macros::task;
12 12
13cfg_if::cfg_if! { 13macro_rules! check_at_most_one {
14 if #[cfg(cortex_m)] { 14 (@amo [$($feats:literal)*] [] [$($res:tt)*]) => {
15 #[path="arch/cortex_m.rs"] 15 #[cfg(any($($res)*))]
16 mod arch; 16 compile_error!(concat!("At most one of these features can be enabled at the same time:", $(" `", $feats, "`",)*));
17 pub use arch::*; 17 };
18 #[cfg(feature = "nightly")] 18 (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => {
19 pub use embassy_macros::main_cortex_m as main; 19 check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]);
20 } 20 };
21 else if #[cfg(target_arch="riscv32")] { 21 ($($f:literal),*$(,)?) => {
22 #[path="arch/riscv32.rs"] 22 check_at_most_one!(@amo [$($f)*] [$($f)*] []);
23 mod arch; 23 };
24 pub use arch::*;
25 #[cfg(feature = "nightly")]
26 pub use embassy_macros::main_riscv as main;
27 }
28 else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] {
29 #[path="arch/xtensa.rs"]
30 mod arch;
31 pub use arch::*;
32 }
33 else if #[cfg(feature="wasm")] {
34 #[path="arch/wasm.rs"]
35 mod arch;
36 pub use arch::*;
37 #[cfg(feature = "nightly")]
38 pub use embassy_macros::main_wasm as main;
39 }
40 else if #[cfg(feature="std")] {
41 #[path="arch/std.rs"]
42 mod arch;
43 pub use arch::*;
44 #[cfg(feature = "nightly")]
45 pub use embassy_macros::main_std as main;
46 }
47} 24}
25check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "arch-wasm",);
26
27#[cfg(feature = "_arch")]
28#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")]
29#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
30#[cfg_attr(feature = "arch-xtensa", path = "arch/xtensa.rs")]
31#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
32#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
33mod arch;
34
35#[cfg(feature = "_arch")]
36pub use arch::*;
48 37
38pub mod raw;
39
40mod spawner;
41pub use spawner::*;
42
43/// Implementation details for embassy macros.
44/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
49#[doc(hidden)] 45#[doc(hidden)]
50/// Implementation details for embassy macros. DO NOT USE. 46pub mod _export {
51pub mod export {
52 #[cfg(feature = "rtos-trace")] 47 #[cfg(feature = "rtos-trace")]
53 pub use rtos_trace::trace; 48 pub use rtos_trace::trace;
49 pub use static_cell::StaticCell;
54 50
55 /// Expands the given block of code when `embassy-executor` is compiled with 51 /// Expands the given block of code when `embassy-executor` is compiled with
56 /// the `rtos-trace-interrupt` feature. 52 /// the `rtos-trace-interrupt` feature.
@@ -70,14 +66,3 @@ pub mod export {
70 ($($tt:tt)*) => {}; 66 ($($tt:tt)*) => {};
71 } 67 }
72} 68}
73
74pub mod raw;
75
76mod spawner;
77pub use spawner::*;
78
79/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
80#[doc(hidden)]
81pub mod _export {
82 pub use static_cell::StaticCell;
83}
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 72c367c33..f6c66da5a 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -19,7 +19,6 @@ use core::marker::PhantomData;
19use core::mem; 19use core::mem;
20use core::pin::Pin; 20use core::pin::Pin;
21use core::ptr::NonNull; 21use core::ptr::NonNull;
22use core::sync::atomic::AtomicPtr;
23use core::task::{Context, Poll}; 22use core::task::{Context, Poll};
24 23
25use atomic_polyfill::{AtomicU32, Ordering}; 24use atomic_polyfill::{AtomicU32, Ordering};
@@ -290,10 +289,60 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
290 } 289 }
291} 290}
292 291
292#[derive(Clone, Copy)]
293pub(crate) enum PenderInner {
294 #[cfg(feature = "executor-thread")]
295 Thread(crate::arch::ThreadPender),
296 #[cfg(feature = "executor-interrupt")]
297 Interrupt(crate::arch::InterruptPender),
298 #[cfg(feature = "pender-callback")]
299 Callback { func: fn(*mut ()), context: *mut () },
300}
301
302unsafe impl Send for PenderInner {}
303unsafe impl Sync for PenderInner {}
304
305/// Platform/architecture-specific action executed when an executor has pending work.
306///
307/// When a task within an executor is woken, the `Pender` is called. This does a
308/// platform/architecture-specific action to signal there is pending work in the executor.
309/// When this happens, you must arrange for [`Executor::poll`] to be called.
310///
311/// You can think of it as a waker, but for the whole executor.
312pub struct Pender(pub(crate) PenderInner);
313
314impl Pender {
315 /// Create a `Pender` that will call an arbitrary function pointer.
316 ///
317 /// # Arguments
318 ///
319 /// - `func`: The function pointer to call.
320 /// - `context`: Opaque context pointer, that will be passed to the function pointer.
321 #[cfg(feature = "pender-callback")]
322 pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self {
323 Self(PenderInner::Callback {
324 func,
325 context: context.into(),
326 })
327 }
328}
329
330impl Pender {
331 pub(crate) fn pend(&self) {
332 match self.0 {
333 #[cfg(feature = "executor-thread")]
334 PenderInner::Thread(x) => x.pend(),
335 #[cfg(feature = "executor-interrupt")]
336 PenderInner::Interrupt(x) => x.pend(),
337 #[cfg(feature = "pender-callback")]
338 PenderInner::Callback { func, context } => func(context),
339 }
340 }
341}
342
293pub(crate) struct SyncExecutor { 343pub(crate) struct SyncExecutor {
294 run_queue: RunQueue, 344 run_queue: RunQueue,
295 signal_fn: fn(*mut ()), 345 pender: Pender,
296 signal_ctx: AtomicPtr<()>,
297 346
298 #[cfg(feature = "integrated-timers")] 347 #[cfg(feature = "integrated-timers")]
299 pub(crate) timer_queue: timer_queue::TimerQueue, 348 pub(crate) timer_queue: timer_queue::TimerQueue,
@@ -302,16 +351,13 @@ pub(crate) struct SyncExecutor {
302} 351}
303 352
304impl SyncExecutor { 353impl SyncExecutor {
305 pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { 354 pub(crate) fn new(pender: Pender) -> Self {
306 #[cfg(feature = "integrated-timers")] 355 #[cfg(feature = "integrated-timers")]
307 let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; 356 let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
308 #[cfg(feature = "integrated-timers")]
309 driver::set_alarm_callback(alarm, signal_fn, signal_ctx);
310 357
311 Self { 358 Self {
312 run_queue: RunQueue::new(), 359 run_queue: RunQueue::new(),
313 signal_fn, 360 pender,
314 signal_ctx: AtomicPtr::new(signal_ctx),
315 361
316 #[cfg(feature = "integrated-timers")] 362 #[cfg(feature = "integrated-timers")]
317 timer_queue: timer_queue::TimerQueue::new(), 363 timer_queue: timer_queue::TimerQueue::new(),
@@ -332,10 +378,16 @@ impl SyncExecutor {
332 trace::task_ready_begin(task.as_ptr() as u32); 378 trace::task_ready_begin(task.as_ptr() as u32);
333 379
334 if self.run_queue.enqueue(cs, task) { 380 if self.run_queue.enqueue(cs, task) {
335 (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) 381 self.pender.pend();
336 } 382 }
337 } 383 }
338 384
385 #[cfg(feature = "integrated-timers")]
386 fn alarm_callback(ctx: *mut ()) {
387 let this: &Self = unsafe { &*(ctx as *const Self) };
388 this.pender.pend();
389 }
390
339 pub(super) unsafe fn spawn(&'static self, task: TaskRef) { 391 pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
340 task.header().executor.set(Some(self)); 392 task.header().executor.set(Some(self));
341 393
@@ -351,6 +403,9 @@ impl SyncExecutor {
351 /// 403 ///
352 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. 404 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
353 pub(crate) unsafe fn poll(&'static self) { 405 pub(crate) unsafe fn poll(&'static self) {
406 #[cfg(feature = "integrated-timers")]
407 driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
408
354 #[allow(clippy::never_loop)] 409 #[allow(clippy::never_loop)]
355 loop { 410 loop {
356 #[cfg(feature = "integrated-timers")] 411 #[cfg(feature = "integrated-timers")]
@@ -417,14 +472,14 @@ impl SyncExecutor {
417/// 472///
418/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks 473/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
419/// that "want to run"). 474/// that "want to run").
420/// - You must supply a `signal_fn`. The executor will call it to notify you it has work 475/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
421/// to do. You must arrange for `poll()` to be called as soon as possible. 476/// to do. You must arrange for `poll()` to be called as soon as possible.
422/// 477///
423/// `signal_fn` can be called from *any* context: any thread, any interrupt priority 478/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority
424/// level, etc. It may be called synchronously from any `Executor` method call as well. 479/// level, etc. It may be called synchronously from any `Executor` method call as well.
425/// You must deal with this correctly. 480/// You must deal with this correctly.
426/// 481///
427/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates 482/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
428/// the requirement for `poll` to not be called reentrantly. 483/// the requirement for `poll` to not be called reentrantly.
429#[repr(transparent)] 484#[repr(transparent)]
430pub struct Executor { 485pub struct Executor {
@@ -437,15 +492,15 @@ impl Executor {
437 pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { 492 pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self {
438 mem::transmute(inner) 493 mem::transmute(inner)
439 } 494 }
495
440 /// Create a new executor. 496 /// Create a new executor.
441 /// 497 ///
442 /// When the executor has work to do, it will call `signal_fn` with 498 /// When the executor has work to do, it will call the [`Pender`].
443 /// `signal_ctx` as argument.
444 /// 499 ///
445 /// See [`Executor`] docs for details on `signal_fn`. 500 /// See [`Executor`] docs for details on `Pender`.
446 pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { 501 pub fn new(pender: Pender) -> Self {
447 Self { 502 Self {
448 inner: SyncExecutor::new(signal_fn, signal_ctx), 503 inner: SyncExecutor::new(pender),
449 _not_sync: PhantomData, 504 _not_sync: PhantomData,
450 } 505 }
451 } 506 }
@@ -468,16 +523,16 @@ impl Executor {
468 /// This loops over all tasks that are queued to be polled (i.e. they're 523 /// This loops over all tasks that are queued to be polled (i.e. they're
469 /// freshly spawned or they've been woken). Other tasks are not polled. 524 /// freshly spawned or they've been woken). Other tasks are not polled.
470 /// 525 ///
471 /// You must call `poll` after receiving a call to `signal_fn`. It is OK 526 /// You must call `poll` after receiving a call to the [`Pender`]. It is OK
472 /// to call `poll` even when not requested by `signal_fn`, but it wastes 527 /// to call `poll` even when not requested by the `Pender`, but it wastes
473 /// energy. 528 /// energy.
474 /// 529 ///
475 /// # Safety 530 /// # Safety
476 /// 531 ///
477 /// You must NOT call `poll` reentrantly on the same executor. 532 /// You must NOT call `poll` reentrantly on the same executor.
478 /// 533 ///
479 /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you 534 /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you
480 /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to 535 /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to
481 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's 536 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
482 /// no `poll()` already running. 537 /// no `poll()` already running.
483 pub unsafe fn poll(&'static self) { 538 pub unsafe fn poll(&'static self) {
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-stm32/build.rs b/embassy-stm32/build.rs
index 3780c5a40..481fec677 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -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("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" => {}
@@ -258,6 +261,7 @@ fn main() {
258 (("i2c", "SCL"), quote!(crate::i2c::SclPin)), 261 (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
259 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)), 262 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
260 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)), 263 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
264 (("rcc", "MCO"), quote!(crate::rcc::McoPin)),
261 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)), 265 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
262 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)), 266 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
263 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)), 267 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
@@ -447,13 +451,22 @@ fn main() {
447 // MCO is special 451 // MCO is special
448 if pin.signal.starts_with("MCO_") { 452 if pin.signal.starts_with("MCO_") {
449 // Supported in H7 only for now 453 // Supported in H7 only for now
450 if regs.version.starts_with("h7") { 454 if regs.version.starts_with("h7") || regs.version.starts_with("f4") {
451 peri = format_ident!("{}", pin.signal.replace("_", "")); 455 peri = format_ident!("{}", pin.signal.replace("_", ""));
452 } else { 456 } else {
453 continue; 457 continue;
454 } 458 }
455 } 459 }
456 460
461 if pin.signal == "MCO" {
462 // Supported in H7 only for now
463 if regs.version.starts_with("l4") {
464 peri = format_ident!("MCO");
465 } else {
466 continue;
467 }
468 }
469
457 g.extend(quote! { 470 g.extend(quote! {
458 pin_trait_impl!(#tr, #peri, #pin_name, #af); 471 pin_trait_impl!(#tr, #peri, #pin_name, #af);
459 }) 472 })
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index 200bcce9c..2a17eb9b0 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -1,8 +1,16 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre};
5
1use super::sealed::RccPeripheral; 6use super::sealed::RccPeripheral;
7use crate::gpio::sealed::AFType;
8use crate::gpio::Speed;
2use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; 9use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
3use crate::pac::{FLASH, PWR, RCC}; 10use crate::pac::{FLASH, PWR, RCC};
4use crate::rcc::{set_freqs, Clocks}; 11use crate::rcc::{set_freqs, Clocks};
5use crate::time::Hertz; 12use crate::time::Hertz;
13use crate::{peripherals, Peripheral};
6 14
7/// HSI speed 15/// HSI speed
8pub const HSI_FREQ: Hertz = Hertz(16_000_000); 16pub const HSI_FREQ: Hertz = Hertz(16_000_000);
@@ -96,6 +104,164 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
96 } 104 }
97} 105}
98 106
107pub enum McoClock {
108 DIV1,
109 DIV2,
110 DIV3,
111 DIV4,
112 DIV5,
113}
114
115impl McoClock {
116 fn into_raw(&self) -> Mcopre {
117 match self {
118 McoClock::DIV1 => Mcopre::DIV1,
119 McoClock::DIV2 => Mcopre::DIV2,
120 McoClock::DIV3 => Mcopre::DIV3,
121 McoClock::DIV4 => Mcopre::DIV4,
122 McoClock::DIV5 => Mcopre::DIV5,
123 }
124 }
125}
126
127#[derive(Copy, Clone)]
128pub enum Mco1Source {
129 Hsi,
130 Lse,
131 Hse,
132 Pll,
133}
134
135impl Default for Mco1Source {
136 fn default() -> Self {
137 Self::Hsi
138 }
139}
140
141pub trait McoSource {
142 type Raw;
143
144 fn into_raw(&self) -> Self::Raw;
145}
146
147impl McoSource for Mco1Source {
148 type Raw = Mco1;
149 fn into_raw(&self) -> Self::Raw {
150 match self {
151 Mco1Source::Hsi => Mco1::HSI,
152 Mco1Source::Lse => Mco1::LSE,
153 Mco1Source::Hse => Mco1::HSE,
154 Mco1Source::Pll => Mco1::PLL,
155 }
156 }
157}
158
159#[derive(Copy, Clone)]
160pub enum Mco2Source {
161 SysClk,
162 Plli2s,
163 Hse,
164 Pll,
165}
166
167impl Default for Mco2Source {
168 fn default() -> Self {
169 Self::SysClk
170 }
171}
172
173impl McoSource for Mco2Source {
174 type Raw = Mco2;
175 fn into_raw(&self) -> Self::Raw {
176 match self {
177 Mco2Source::SysClk => Mco2::SYSCLK,
178 Mco2Source::Plli2s => Mco2::PLLI2S,
179 Mco2Source::Hse => Mco2::HSE,
180 Mco2Source::Pll => Mco2::PLL,
181 }
182 }
183}
184
185pub(crate) mod sealed {
186 use stm32_metapac::rcc::vals::Mcopre;
187 pub trait McoInstance {
188 type Source;
189 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
190 }
191}
192
193pub trait McoInstance: sealed::McoInstance + 'static {}
194
195pin_trait!(McoPin, McoInstance);
196
197impl sealed::McoInstance for peripherals::MCO1 {
198 type Source = Mco1;
199 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
200 RCC.cfgr().modify(|w| {
201 w.set_mco1(source);
202 w.set_mco1pre(prescaler);
203 });
204 match source {
205 Mco1::PLL => {
206 RCC.cr().modify(|w| w.set_pllon(true));
207 while !RCC.cr().read().pllrdy() {}
208 }
209 Mco1::HSI => {
210 RCC.cr().modify(|w| w.set_hsion(true));
211 while !RCC.cr().read().hsirdy() {}
212 }
213 _ => {}
214 }
215 }
216}
217impl McoInstance for peripherals::MCO1 {}
218
219impl sealed::McoInstance for peripherals::MCO2 {
220 type Source = Mco2;
221 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
222 RCC.cfgr().modify(|w| {
223 w.set_mco2(source);
224 w.set_mco2pre(prescaler);
225 });
226 match source {
227 Mco2::PLL => {
228 RCC.cr().modify(|w| w.set_pllon(true));
229 while !RCC.cr().read().pllrdy() {}
230 }
231 #[cfg(not(stm32f410))]
232 Mco2::PLLI2S => {
233 RCC.cr().modify(|w| w.set_plli2son(true));
234 while !RCC.cr().read().plli2srdy() {}
235 }
236 _ => {}
237 }
238 }
239}
240impl McoInstance for peripherals::MCO2 {}
241
242pub struct Mco<'d, T: McoInstance> {
243 phantom: PhantomData<&'d mut T>,
244}
245
246impl<'d, T: McoInstance> Mco<'d, T> {
247 pub fn new(
248 _peri: impl Peripheral<P = T> + 'd,
249 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
250 source: impl McoSource<Raw = T::Source>,
251 prescaler: McoClock,
252 ) -> Self {
253 into_ref!(pin);
254
255 critical_section::with(|_| unsafe {
256 T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
257 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
258 pin.set_speed(Speed::VeryHigh);
259 });
260
261 Self { phantom: PhantomData }
262 }
263}
264
99unsafe fn flash_setup(sysclk: u32) { 265unsafe fn flash_setup(sysclk: u32) {
100 use crate::pac::flash::vals::Latency; 266 use crate::pac::flash::vals::Latency;
101 267
diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs
index e650490fe..c1bf7d0cd 100644
--- a/embassy-stm32/src/rcc/l4.rs
+++ b/embassy-stm32/src/rcc/l4.rs
@@ -1,7 +1,15 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use stm32_metapac::rcc::vals::{Mcopre, Mcosel};
5
6use crate::gpio::sealed::AFType;
7use crate::gpio::Speed;
1use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; 8use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
2use crate::pac::{FLASH, RCC}; 9use crate::pac::{FLASH, RCC};
3use crate::rcc::{set_freqs, Clocks}; 10use crate::rcc::{set_freqs, Clocks};
4use crate::time::Hertz; 11use crate::time::Hertz;
12use crate::{peripherals, Peripheral};
5 13
6/// HSI speed 14/// HSI speed
7pub const HSI_FREQ: Hertz = Hertz(16_000_000); 15pub const HSI_FREQ: Hertz = Hertz(16_000_000);
@@ -298,6 +306,131 @@ impl Default for Config {
298 } 306 }
299} 307}
300 308
309pub enum McoClock {
310 DIV1,
311 DIV2,
312 DIV4,
313 DIV8,
314 DIV16,
315}
316
317impl McoClock {
318 fn into_raw(&self) -> Mcopre {
319 match self {
320 McoClock::DIV1 => Mcopre::DIV1,
321 McoClock::DIV2 => Mcopre::DIV2,
322 McoClock::DIV4 => Mcopre::DIV4,
323 McoClock::DIV8 => Mcopre::DIV8,
324 McoClock::DIV16 => Mcopre::DIV16,
325 }
326 }
327}
328
329#[derive(Copy, Clone)]
330pub enum Mco1Source {
331 Disabled,
332 Lse,
333 Lsi,
334 Hse,
335 Hsi16,
336 PllClk,
337 SysClk,
338 Msi,
339 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
340 Hsi48,
341}
342
343impl Default for Mco1Source {
344 fn default() -> Self {
345 Self::Hsi16
346 }
347}
348
349pub trait McoSource {
350 type Raw;
351
352 fn into_raw(&self) -> Self::Raw;
353}
354
355impl McoSource for Mco1Source {
356 type Raw = Mcosel;
357 fn into_raw(&self) -> Self::Raw {
358 match self {
359 Mco1Source::Disabled => Mcosel::NOCLOCK,
360 Mco1Source::Lse => Mcosel::LSE,
361 Mco1Source::Lsi => Mcosel::LSI,
362 Mco1Source::Hse => Mcosel::HSE,
363 Mco1Source::Hsi16 => Mcosel::HSI16,
364 Mco1Source::PllClk => Mcosel::PLL,
365 Mco1Source::SysClk => Mcosel::SYSCLK,
366 Mco1Source::Msi => Mcosel::MSI,
367 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
368 Mco1Source::Hsi48 => Mcosel::HSI48,
369 }
370 }
371}
372
373pub(crate) mod sealed {
374 use stm32_metapac::rcc::vals::Mcopre;
375 pub trait McoInstance {
376 type Source;
377 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
378 }
379}
380
381pub trait McoInstance: sealed::McoInstance + 'static {}
382
383pin_trait!(McoPin, McoInstance);
384
385impl sealed::McoInstance for peripherals::MCO {
386 type Source = Mcosel;
387
388 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
389 RCC.cfgr().modify(|w| {
390 w.set_mcosel(source);
391 w.set_mcopre(prescaler);
392 });
393
394 match source {
395 Mcosel::HSI16 => {
396 RCC.cr().modify(|w| w.set_hsion(true));
397 while !RCC.cr().read().hsirdy() {}
398 }
399 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
400 Mcosel::HSI48 => {
401 RCC.crrcr().modify(|w| w.set_hsi48on(true));
402 while !RCC.crrcr().read().hsi48rdy() {}
403 }
404 _ => {}
405 }
406 }
407}
408
409impl McoInstance for peripherals::MCO {}
410
411pub struct Mco<'d, T: McoInstance> {
412 phantom: PhantomData<&'d mut T>,
413}
414
415impl<'d, T: McoInstance> Mco<'d, T> {
416 pub fn new(
417 _peri: impl Peripheral<P = T> + 'd,
418 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
419 source: impl McoSource<Raw = T::Source>,
420 prescaler: McoClock,
421 ) -> Self {
422 into_ref!(pin);
423
424 critical_section::with(|_| unsafe {
425 T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
426 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
427 pin.set_speed(Speed::VeryHigh);
428 });
429
430 Self { phantom: PhantomData }
431 }
432}
433
301pub(crate) unsafe fn init(config: Config) { 434pub(crate) unsafe fn init(config: Config) {
302 let (sys_clk, sw) = match config.mux { 435 let (sys_clk, sw) = match config.mux {
303 ClockSrc::MSI(range) => { 436 ClockSrc::MSI(range) => {
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index cd7d72f91..3e23e7ca1 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -1,55 +1,51 @@
1use core::cell::RefCell;
2use core::future::poll_fn; 1use core::future::poll_fn;
3use core::sync::atomic::{compiler_fence, Ordering}; 2use core::slice;
4use core::task::Poll; 3use core::task::Poll;
5 4
6use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; 5use embassy_cortex_m::interrupt::Interrupt;
7use embassy_hal_common::ring_buffer::RingBuffer; 6use embassy_hal_common::atomic_ring_buffer::RingBuffer;
8use embassy_sync::waitqueue::WakerRegistration; 7use embassy_sync::waitqueue::AtomicWaker;
9 8
10use super::*; 9use super::*;
11 10
12pub struct State<'d, T: BasicInstance>(StateStorage<StateInner<'d, T>>); 11pub struct State {
13impl<'d, T: BasicInstance> State<'d, T> { 12 rx_waker: AtomicWaker,
14 pub const fn new() -> Self { 13 rx_buf: RingBuffer,
15 Self(StateStorage::new())
16 }
17}
18
19struct StateInner<'d, T: BasicInstance> {
20 phantom: PhantomData<&'d mut T>,
21
22 rx_waker: WakerRegistration,
23 rx: RingBuffer<'d>,
24 14
25 tx_waker: WakerRegistration, 15 tx_waker: AtomicWaker,
26 tx: RingBuffer<'d>, 16 tx_buf: RingBuffer,
27} 17}
28 18
29unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} 19impl State {
30unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {} 20 pub const fn new() -> Self {
21 Self {
22 rx_buf: RingBuffer::new(),
23 tx_buf: RingBuffer::new(),
24 rx_waker: AtomicWaker::new(),
25 tx_waker: AtomicWaker::new(),
26 }
27 }
28}
31 29
32pub struct BufferedUart<'d, T: BasicInstance> { 30pub struct BufferedUart<'d, T: BasicInstance> {
33 inner: RefCell<PeripheralMutex<'d, StateInner<'d, T>>>, 31 rx: BufferedUartRx<'d, T>,
32 tx: BufferedUartTx<'d, T>,
34} 33}
35 34
36pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { 35pub struct BufferedUartTx<'d, T: BasicInstance> {
37 inner: &'u BufferedUart<'d, T>, 36 phantom: PhantomData<&'d mut T>,
38} 37}
39 38
40pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { 39pub struct BufferedUartRx<'d, T: BasicInstance> {
41 inner: &'u BufferedUart<'d, T>, 40 phantom: PhantomData<&'d mut T>,
42} 41}
43 42
44impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {}
45
46impl<'d, T: BasicInstance> BufferedUart<'d, T> { 43impl<'d, T: BasicInstance> BufferedUart<'d, T> {
47 pub fn new( 44 pub fn new(
48 state: &'d mut State<'d, T>,
49 peri: impl Peripheral<P = T> + 'd, 45 peri: impl Peripheral<P = T> + 'd,
46 irq: impl Peripheral<P = T::Interrupt> + 'd,
50 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 47 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
51 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 48 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
52 irq: impl Peripheral<P = T::Interrupt> + 'd,
53 tx_buffer: &'d mut [u8], 49 tx_buffer: &'d mut [u8],
54 rx_buffer: &'d mut [u8], 50 rx_buffer: &'d mut [u8],
55 config: Config, 51 config: Config,
@@ -57,15 +53,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
57 T::enable(); 53 T::enable();
58 T::reset(); 54 T::reset();
59 55
60 Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) 56 Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
61 } 57 }
62 58
63 pub fn new_with_rtscts( 59 pub fn new_with_rtscts(
64 state: &'d mut State<'d, T>,
65 peri: impl Peripheral<P = T> + 'd, 60 peri: impl Peripheral<P = T> + 'd,
61 irq: impl Peripheral<P = T::Interrupt> + 'd,
66 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 62 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
67 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 63 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
68 irq: impl Peripheral<P = T::Interrupt> + 'd,
69 rts: impl Peripheral<P = impl RtsPin<T>> + 'd, 64 rts: impl Peripheral<P = impl RtsPin<T>> + 'd,
70 cts: impl Peripheral<P = impl CtsPin<T>> + 'd, 65 cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
71 tx_buffer: &'d mut [u8], 66 tx_buffer: &'d mut [u8],
@@ -86,16 +81,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
86 }); 81 });
87 } 82 }
88 83
89 Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) 84 Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
90 } 85 }
91 86
92 #[cfg(not(usart_v1))] 87 #[cfg(not(usart_v1))]
93 pub fn new_with_de( 88 pub fn new_with_de(
94 state: &'d mut State<'d, T>,
95 peri: impl Peripheral<P = T> + 'd, 89 peri: impl Peripheral<P = T> + 'd,
90 irq: impl Peripheral<P = T::Interrupt> + 'd,
96 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 91 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
97 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 92 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
98 irq: impl Peripheral<P = T::Interrupt> + 'd,
99 de: impl Peripheral<P = impl DePin<T>> + 'd, 93 de: impl Peripheral<P = impl DePin<T>> + 'd,
100 tx_buffer: &'d mut [u8], 94 tx_buffer: &'d mut [u8],
101 rx_buffer: &'d mut [u8], 95 rx_buffer: &'d mut [u8],
@@ -113,23 +107,27 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
113 }); 107 });
114 } 108 }
115 109
116 Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) 110 Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config)
117 } 111 }
118 112
119 fn new_inner( 113 fn new_inner(
120 state: &'d mut State<'d, T>,
121 _peri: impl Peripheral<P = T> + 'd, 114 _peri: impl Peripheral<P = T> + 'd,
115 irq: impl Peripheral<P = T::Interrupt> + 'd,
122 rx: impl Peripheral<P = impl RxPin<T>> + 'd, 116 rx: impl Peripheral<P = impl RxPin<T>> + 'd,
123 tx: impl Peripheral<P = impl TxPin<T>> + 'd, 117 tx: impl Peripheral<P = impl TxPin<T>> + 'd,
124 irq: impl Peripheral<P = T::Interrupt> + 'd,
125 tx_buffer: &'d mut [u8], 118 tx_buffer: &'d mut [u8],
126 rx_buffer: &'d mut [u8], 119 rx_buffer: &'d mut [u8],
127 config: Config, 120 config: Config,
128 ) -> BufferedUart<'d, T> { 121 ) -> BufferedUart<'d, T> {
129 into_ref!(_peri, rx, tx, irq); 122 into_ref!(_peri, rx, tx, irq);
130 123
131 let r = T::regs(); 124 let state = T::buffered_state();
125 let len = tx_buffer.len();
126 unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
127 let len = rx_buffer.len();
128 unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
132 129
130 let r = T::regs();
133 unsafe { 131 unsafe {
134 rx.set_as_af(rx.af_num(), AFType::Input); 132 rx.set_as_af(rx.af_num(), AFType::Input);
135 tx.set_as_af(tx.af_num(), AFType::OutputPushPull); 133 tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
@@ -147,273 +145,259 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
147 }); 145 });
148 } 146 }
149 147
150 Self { 148 irq.set_handler(on_interrupt::<T>);
151 inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { 149 irq.unpend();
152 phantom: PhantomData, 150 irq.enable();
153 tx: RingBuffer::new(tx_buffer),
154 tx_waker: WakerRegistration::new(),
155 151
156 rx: RingBuffer::new(rx_buffer), 152 Self {
157 rx_waker: WakerRegistration::new(), 153 rx: BufferedUartRx { phantom: PhantomData },
158 })), 154 tx: BufferedUartTx { phantom: PhantomData },
159 } 155 }
160 } 156 }
161 157
162 pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) { 158 pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) {
163 (BufferedUartRx { inner: self }, BufferedUartTx { inner: self }) 159 (self.tx, self.rx)
164 } 160 }
161}
165 162
166 async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, Error> { 163impl<'d, T: BasicInstance> BufferedUartRx<'d, T> {
164 async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
167 poll_fn(move |cx| { 165 poll_fn(move |cx| {
168 let mut do_pend = false; 166 let state = T::buffered_state();
169 let mut inner = self.inner.borrow_mut(); 167 let mut rx_reader = unsafe { state.rx_buf.reader() };
170 let res = inner.with(|state| { 168 let data = rx_reader.pop_slice();
171 compiler_fence(Ordering::SeqCst);
172
173 // We have data ready in buffer? Return it.
174 let data = state.rx.pop_buf();
175 if !data.is_empty() {
176 let len = data.len().min(buf.len());
177 buf[..len].copy_from_slice(&data[..len]);
178
179 if state.rx.is_full() {
180 do_pend = true;
181 }
182 state.rx.pop(len);
183
184 return Poll::Ready(Ok(len));
185 }
186 169
187 state.rx_waker.register(cx.waker()); 170 if !data.is_empty() {
188 Poll::Pending 171 let len = data.len().min(buf.len());
189 }); 172 buf[..len].copy_from_slice(&data[..len]);
173
174 let do_pend = state.rx_buf.is_full();
175 rx_reader.pop_done(len);
190 176
191 if do_pend { 177 if do_pend {
192 inner.pend(); 178 unsafe { T::Interrupt::steal().pend() };
179 }
180
181 return Poll::Ready(Ok(len));
193 } 182 }
194 183
195 res 184 state.rx_waker.register(cx.waker());
185 Poll::Pending
196 }) 186 })
197 .await 187 .await
198 } 188 }
199 189
200 fn inner_blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> { 190 fn blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
201 loop { 191 loop {
202 let mut do_pend = false; 192 let state = T::buffered_state();
203 let mut inner = self.inner.borrow_mut(); 193 let mut rx_reader = unsafe { state.rx_buf.reader() };
204 let n = inner.with(|state| { 194 let data = rx_reader.pop_slice();
205 compiler_fence(Ordering::SeqCst);
206
207 // We have data ready in buffer? Return it.
208 let data = state.rx.pop_buf();
209 if !data.is_empty() {
210 let len = data.len().min(buf.len());
211 buf[..len].copy_from_slice(&data[..len]);
212
213 if state.rx.is_full() {
214 do_pend = true;
215 }
216 state.rx.pop(len);
217
218 return len;
219 }
220 195
221 0 196 if !data.is_empty() {
222 }); 197 let len = data.len().min(buf.len());
198 buf[..len].copy_from_slice(&data[..len]);
199
200 let do_pend = state.rx_buf.is_full();
201 rx_reader.pop_done(len);
202
203 if do_pend {
204 unsafe { T::Interrupt::steal().pend() };
205 }
223 206
224 if do_pend { 207 return Ok(len);
225 inner.pend();
226 } 208 }
209 }
210 }
227 211
228 if n > 0 { 212 async fn fill_buf(&self) -> Result<&[u8], Error> {
229 return Ok(n); 213 poll_fn(move |cx| {
214 let state = T::buffered_state();
215 let mut rx_reader = unsafe { state.rx_buf.reader() };
216 let (p, n) = rx_reader.pop_buf();
217 if n == 0 {
218 state.rx_waker.register(cx.waker());
219 return Poll::Pending;
230 } 220 }
221
222 let buf = unsafe { slice::from_raw_parts(p, n) };
223 Poll::Ready(Ok(buf))
224 })
225 .await
226 }
227
228 fn consume(&self, amt: usize) {
229 let state = T::buffered_state();
230 let mut rx_reader = unsafe { state.rx_buf.reader() };
231 let full = state.rx_buf.is_full();
232 rx_reader.pop_done(amt);
233 if full {
234 unsafe { T::Interrupt::steal().pend() };
231 } 235 }
232 } 236 }
237}
233 238
234 async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> { 239impl<'d, T: BasicInstance> BufferedUartTx<'d, T> {
240 async fn write(&self, buf: &[u8]) -> Result<usize, Error> {
235 poll_fn(move |cx| { 241 poll_fn(move |cx| {
236 let mut inner = self.inner.borrow_mut(); 242 let state = T::buffered_state();
237 let (poll, empty) = inner.with(|state| { 243 let empty = state.tx_buf.is_empty();
238 let empty = state.tx.is_empty(); 244
239 let tx_buf = state.tx.push_buf(); 245 let mut tx_writer = unsafe { state.tx_buf.writer() };
240 if tx_buf.is_empty() { 246 let data = tx_writer.push_slice();
241 state.tx_waker.register(cx.waker()); 247 if data.is_empty() {
242 return (Poll::Pending, empty); 248 state.tx_waker.register(cx.waker());
243 } 249 return Poll::Pending;
250 }
244 251
245 let n = core::cmp::min(tx_buf.len(), buf.len()); 252 let n = data.len().min(buf.len());
246 tx_buf[..n].copy_from_slice(&buf[..n]); 253 data[..n].copy_from_slice(&buf[..n]);
247 state.tx.push(n); 254 tx_writer.push_done(n);
248 255
249 (Poll::Ready(Ok(n)), empty)
250 });
251 if empty { 256 if empty {
252 inner.pend(); 257 unsafe { T::Interrupt::steal() }.pend();
253 } 258 }
254 poll 259
260 Poll::Ready(Ok(n))
255 }) 261 })
256 .await 262 .await
257 } 263 }
258 264
259 async fn inner_flush<'a>(&'a self) -> Result<(), Error> { 265 async fn flush(&self) -> Result<(), Error> {
260 poll_fn(move |cx| { 266 poll_fn(move |cx| {
261 self.inner.borrow_mut().with(|state| { 267 let state = T::buffered_state();
262 if !state.tx.is_empty() { 268 if !state.tx_buf.is_empty() {
263 state.tx_waker.register(cx.waker()); 269 state.tx_waker.register(cx.waker());
264 return Poll::Pending; 270 return Poll::Pending;
265 } 271 }
266 272
267 Poll::Ready(Ok(())) 273 Poll::Ready(Ok(()))
268 })
269 }) 274 })
270 .await 275 .await
271 } 276 }
272 277
273 fn inner_blocking_write(&self, buf: &[u8]) -> Result<usize, Error> { 278 fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> {
274 loop { 279 loop {
275 let mut inner = self.inner.borrow_mut(); 280 let state = T::buffered_state();
276 let (n, empty) = inner.with(|state| { 281 let empty = state.tx_buf.is_empty();
277 let empty = state.tx.is_empty(); 282
278 let tx_buf = state.tx.push_buf(); 283 let mut tx_writer = unsafe { state.tx_buf.writer() };
279 if tx_buf.is_empty() { 284 let data = tx_writer.push_slice();
280 return (0, empty); 285 if !data.is_empty() {
286 let n = data.len().min(buf.len());
287 data[..n].copy_from_slice(&buf[..n]);
288 tx_writer.push_done(n);
289
290 if empty {
291 unsafe { T::Interrupt::steal() }.pend();
281 } 292 }
282 293
283 let n = core::cmp::min(tx_buf.len(), buf.len());
284 tx_buf[..n].copy_from_slice(&buf[..n]);
285 state.tx.push(n);
286
287 (n, empty)
288 });
289 if empty {
290 inner.pend();
291 }
292 if n != 0 {
293 return Ok(n); 294 return Ok(n);
294 } 295 }
295 } 296 }
296 } 297 }
297 298
298 fn inner_blocking_flush(&self) -> Result<(), Error> { 299 fn blocking_flush(&self) -> Result<(), Error> {
299 loop { 300 loop {
300 if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) { 301 let state = T::buffered_state();
302 if state.tx_buf.is_empty() {
301 return Ok(()); 303 return Ok(());
302 } 304 }
303 } 305 }
304 } 306 }
307}
305 308
306 async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { 309impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> {
307 poll_fn(move |cx| { 310 fn drop(&mut self) {
308 self.inner.borrow_mut().with(|state| { 311 let state = T::buffered_state();
309 compiler_fence(Ordering::SeqCst); 312 unsafe {
310 313 state.rx_buf.deinit();
311 // We have data ready in buffer? Return it.
312 let buf = state.rx.pop_buf();
313 if !buf.is_empty() {
314 let buf: &[u8] = buf;
315 // Safety: buffer lives as long as uart
316 let buf: &[u8] = unsafe { core::mem::transmute(buf) };
317 return Poll::Ready(Ok(buf));
318 }
319
320 state.rx_waker.register(cx.waker());
321 Poll::<Result<&[u8], Error>>::Pending
322 })
323 })
324 .await
325 }
326 314
327 fn inner_consume(&self, amt: usize) { 315 // TX is inactive if the the buffer is not available.
328 let mut inner = self.inner.borrow_mut(); 316 // We can now unregister the interrupt handler
329 let signal = inner.with(|state| { 317 if state.tx_buf.len() == 0 {
330 let full = state.rx.is_full(); 318 T::Interrupt::steal().disable();
331 state.rx.pop(amt); 319 }
332 full
333 });
334 if signal {
335 inner.pend();
336 } 320 }
337 } 321 }
338} 322}
339 323
340impl<'d, T: BasicInstance> StateInner<'d, T> 324impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> {
341where 325 fn drop(&mut self) {
342 Self: 'd, 326 let state = T::buffered_state();
343{
344 fn on_rx(&mut self) {
345 let r = T::regs();
346 unsafe { 327 unsafe {
347 let sr = sr(r).read(); 328 state.tx_buf.deinit();
348 clear_interrupt_flags(r, sr);
349 329
350 // This read also clears the error and idle interrupt flags on v1. 330 // RX is inactive if the the buffer is not available.
351 let b = rdr(r).read_volatile(); 331 // We can now unregister the interrupt handler
332 if state.rx_buf.len() == 0 {
333 T::Interrupt::steal().disable();
334 }
335 }
336 }
337}
352 338
353 if sr.rxne() { 339unsafe fn on_interrupt<T: BasicInstance>(_: *mut ()) {
354 if sr.pe() { 340 let r = T::regs();
355 warn!("Parity error"); 341 let state = T::buffered_state();
356 }
357 if sr.fe() {
358 warn!("Framing error");
359 }
360 if sr.ne() {
361 warn!("Noise error");
362 }
363 if sr.ore() {
364 warn!("Overrun error");
365 }
366 342
367 let buf = self.rx.push_buf(); 343 // RX
368 if !buf.is_empty() { 344 unsafe {
369 buf[0] = b; 345 let sr = sr(r).read();
370 self.rx.push(1); 346 clear_interrupt_flags(r, sr);
371 } else {
372 warn!("RX buffer full, discard received byte");
373 }
374 347
375 if self.rx.is_full() { 348 if sr.rxne() {
376 self.rx_waker.wake(); 349 if sr.pe() {
377 } 350 warn!("Parity error");
351 }
352 if sr.fe() {
353 warn!("Framing error");
354 }
355 if sr.ne() {
356 warn!("Noise error");
357 }
358 if sr.ore() {
359 warn!("Overrun error");
378 } 360 }
379 361
380 if sr.idle() { 362 let mut rx_writer = state.rx_buf.writer();
381 self.rx_waker.wake(); 363 let buf = rx_writer.push_slice();
382 }; 364 if !buf.is_empty() {
383 } 365 // This read also clears the error and idle interrupt flags on v1.
384 } 366 buf[0] = rdr(r).read_volatile();
367 rx_writer.push_done(1);
368 } else {
369 // FIXME: Should we disable any further RX interrupts when the buffer becomes full.
370 }
385 371
386 fn on_tx(&mut self) { 372 if state.rx_buf.is_full() {
387 let r = T::regs(); 373 state.rx_waker.wake();
388 unsafe {
389 if sr(r).read().txe() {
390 let buf = self.tx.pop_buf();
391 if !buf.is_empty() {
392 r.cr1().modify(|w| {
393 w.set_txeie(true);
394 });
395 tdr(r).write_volatile(buf[0].into());
396 self.tx.pop(1);
397 self.tx_waker.wake();
398 } else {
399 // Disable interrupt until we have something to transmit again
400 r.cr1().modify(|w| {
401 w.set_txeie(false);
402 });
403 }
404 } 374 }
405 } 375 }
406 }
407}
408 376
409impl<'d, T: BasicInstance> PeripheralState for StateInner<'d, T> 377 if sr.idle() {
410where 378 state.rx_waker.wake();
411 Self: 'd, 379 };
412{ 380 }
413 type Interrupt = T::Interrupt; 381
414 fn on_interrupt(&mut self) { 382 // TX
415 self.on_rx(); 383 unsafe {
416 self.on_tx(); 384 if sr(r).read().txe() {
385 let mut tx_reader = state.tx_buf.reader();
386 let buf = tx_reader.pop_slice();
387 if !buf.is_empty() {
388 r.cr1().modify(|w| {
389 w.set_txeie(true);
390 });
391 tdr(r).write_volatile(buf[0].into());
392 tx_reader.pop_done(1);
393 state.tx_waker.wake();
394 } else {
395 // Disable interrupt until we have something to transmit again
396 r.cr1().modify(|w| {
397 w.set_txeie(false);
398 });
399 }
400 }
417 } 401 }
418} 402}
419 403
@@ -427,94 +411,284 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> {
427 type Error = Error; 411 type Error = Error;
428} 412}
429 413
430impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { 414impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> {
431 type Error = Error; 415 type Error = Error;
432} 416}
433 417
434impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { 418impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> {
435 type Error = Error; 419 type Error = Error;
436} 420}
437 421
438impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { 422impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> {
439 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 423 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
440 self.inner_read(buf).await 424 self.rx.read(buf).await
441 } 425 }
442} 426}
443 427
444impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { 428impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> {
445 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 429 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
446 self.inner.inner_read(buf).await 430 Self::read(self, buf).await
447 } 431 }
448} 432}
449 433
450impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { 434impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
451 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 435 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
452 self.inner_fill_buf().await 436 self.rx.fill_buf().await
453 } 437 }
454 438
455 fn consume(&mut self, amt: usize) { 439 fn consume(&mut self, amt: usize) {
456 self.inner_consume(amt) 440 self.rx.consume(amt)
457 } 441 }
458} 442}
459 443
460impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { 444impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> {
461 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 445 async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
462 self.inner.inner_fill_buf().await 446 Self::fill_buf(self).await
463 } 447 }
464 448
465 fn consume(&mut self, amt: usize) { 449 fn consume(&mut self, amt: usize) {
466 self.inner.inner_consume(amt) 450 Self::consume(self, amt)
467 } 451 }
468} 452}
469 453
470impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { 454impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
471 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 455 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
472 self.inner_write(buf).await 456 self.tx.write(buf).await
473 } 457 }
474 458
475 async fn flush(&mut self) -> Result<(), Self::Error> { 459 async fn flush(&mut self) -> Result<(), Self::Error> {
476 self.inner_flush().await 460 self.tx.flush().await
477 } 461 }
478} 462}
479 463
480impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { 464impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> {
481 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 465 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
482 self.inner.inner_write(buf).await 466 Self::write(self, buf).await
483 } 467 }
484 468
485 async fn flush(&mut self) -> Result<(), Self::Error> { 469 async fn flush(&mut self) -> Result<(), Self::Error> {
486 self.inner.inner_flush().await 470 Self::flush(self).await
487 } 471 }
488} 472}
489 473
490impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { 474impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> {
491 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 475 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
492 self.inner_blocking_read(buf) 476 self.rx.blocking_read(buf)
493 } 477 }
494} 478}
495 479
496impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { 480impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> {
497 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 481 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
498 self.inner.inner_blocking_read(buf) 482 self.blocking_read(buf)
499 } 483 }
500} 484}
501 485
502impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { 486impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> {
503 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 487 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
504 self.inner_blocking_write(buf) 488 self.tx.blocking_write(buf)
505 } 489 }
506 490
507 fn flush(&mut self) -> Result<(), Self::Error> { 491 fn flush(&mut self) -> Result<(), Self::Error> {
508 self.inner_blocking_flush() 492 self.tx.blocking_flush()
509 } 493 }
510} 494}
511 495
512impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { 496impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> {
513 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> { 497 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
514 self.inner.inner_blocking_write(buf) 498 Self::blocking_write(self, buf)
515 } 499 }
516 500
517 fn flush(&mut self) -> Result<(), Self::Error> { 501 fn flush(&mut self) -> Result<(), Self::Error> {
518 self.inner.inner_blocking_flush() 502 Self::blocking_flush(self)
503 }
504}
505
506mod eh02 {
507 use super::*;
508
509 impl<'d, T: BasicInstance> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d, T> {
510 type Error = Error;
511
512 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
513 let r = T::regs();
514 unsafe {
515 let sr = sr(r).read();
516 if sr.pe() {
517 rdr(r).read_volatile();
518 Err(nb::Error::Other(Error::Parity))
519 } else if sr.fe() {
520 rdr(r).read_volatile();
521 Err(nb::Error::Other(Error::Framing))
522 } else if sr.ne() {
523 rdr(r).read_volatile();
524 Err(nb::Error::Other(Error::Noise))
525 } else if sr.ore() {
526 rdr(r).read_volatile();
527 Err(nb::Error::Other(Error::Overrun))
528 } else if sr.rxne() {
529 Ok(rdr(r).read_volatile())
530 } else {
531 Err(nb::Error::WouldBlock)
532 }
533 }
534 }
535 }
536
537 impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d, T> {
538 type Error = Error;
539
540 fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
541 while !buffer.is_empty() {
542 match self.blocking_write(buffer) {
543 Ok(0) => panic!("zero-length write."),
544 Ok(n) => buffer = &buffer[n..],
545 Err(e) => return Err(e),
546 }
547 }
548 Ok(())
549 }
550
551 fn bflush(&mut self) -> Result<(), Self::Error> {
552 self.blocking_flush()
553 }
554 }
555
556 impl<'d, T: BasicInstance> embedded_hal_02::serial::Read<u8> for BufferedUart<'d, T> {
557 type Error = Error;
558
559 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
560 embedded_hal_02::serial::Read::read(&mut self.rx)
561 }
562 }
563
564 impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d, T> {
565 type Error = Error;
566
567 fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {
568 while !buffer.is_empty() {
569 match self.tx.blocking_write(buffer) {
570 Ok(0) => panic!("zero-length write."),
571 Ok(n) => buffer = &buffer[n..],
572 Err(e) => return Err(e),
573 }
574 }
575 Ok(())
576 }
577
578 fn bflush(&mut self) -> Result<(), Self::Error> {
579 self.tx.blocking_flush()
580 }
581 }
582}
583
584#[cfg(feature = "unstable-traits")]
585mod eh1 {
586 use super::*;
587
588 impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> {
589 type Error = Error;
590 }
591
592 impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> {
593 type Error = Error;
594 }
595
596 impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> {
597 type Error = Error;
598 }
599
600 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> {
601 fn read(&mut self) -> nb::Result<u8, Self::Error> {
602 embedded_hal_02::serial::Read::read(self)
603 }
604 }
605
606 impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> {
607 fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
608 self.blocking_write(buffer).map(drop)
609 }
610
611 fn flush(&mut self) -> Result<(), Self::Error> {
612 self.blocking_flush()
613 }
614 }
615
616 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
617 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
618 self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
619 }
620
621 fn flush(&mut self) -> nb::Result<(), Self::Error> {
622 self.blocking_flush().map_err(nb::Error::Other)
623 }
624 }
625
626 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> {
627 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
628 embedded_hal_02::serial::Read::read(&mut self.rx)
629 }
630 }
631
632 impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> {
633 fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
634 self.tx.blocking_write(buffer).map(drop)
635 }
636
637 fn flush(&mut self) -> Result<(), Self::Error> {
638 self.tx.blocking_flush()
639 }
640 }
641
642 impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> {
643 fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
644 self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
645 }
646
647 fn flush(&mut self) -> nb::Result<(), Self::Error> {
648 self.tx.blocking_flush().map_err(nb::Error::Other)
649 }
650 }
651}
652
653#[cfg(all(
654 feature = "unstable-traits",
655 feature = "nightly",
656 feature = "_todo_embedded_hal_serial"
657))]
658mod eha {
659 use core::future::Future;
660
661 use super::*;
662
663 impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> {
664 async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
665 Self::write(buf)
666 }
667
668 async fn flush(&mut self) -> Result<(), Self::Error> {
669 Self::flush()
670 }
671 }
672
673 impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> {
674 async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
675 Self::read(buf)
676 }
677 }
678
679 impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUart<'d, T> {
680 async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
681 self.tx.write(buf)
682 }
683
684 async fn flush(&mut self) -> Result<(), Self::Error> {
685 self.tx.flush()
686 }
687 }
688
689 impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUart<'d, T> {
690 async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
691 self.rx.read(buf)
692 }
519 } 693 }
520} 694}
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index f80323e37..a42eede18 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -1112,6 +1112,9 @@ pub(crate) mod sealed {
1112 1112
1113 fn regs() -> Regs; 1113 fn regs() -> Regs;
1114 fn state() -> &'static State; 1114 fn state() -> &'static State;
1115
1116 #[cfg(feature = "nightly")]
1117 fn buffered_state() -> &'static buffered::State;
1115 } 1118 }
1116 1119
1117 pub trait FullInstance: BasicInstance { 1120 pub trait FullInstance: BasicInstance {
@@ -1147,6 +1150,12 @@ macro_rules! impl_lpuart {
1147 static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); 1150 static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new();
1148 &STATE 1151 &STATE
1149 } 1152 }
1153
1154 #[cfg(feature = "nightly")]
1155 fn buffered_state() -> &'static buffered::State {
1156 static STATE: buffered::State = buffered::State::new();
1157 &STATE
1158 }
1150 } 1159 }
1151 1160
1152 impl BasicInstance for peripherals::$inst {} 1161 impl BasicInstance for peripherals::$inst {}
diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml
index 888993255..e75c73cbd 100644
--- a/examples/boot/application/nrf/Cargo.toml
+++ b/examples/boot/application/nrf/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
11embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } 11embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
12embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } 12embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" }
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index 8d826790b..8de2d2ebd 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] }
11embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } 11embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] }
12embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" } 12embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" }
diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml
index aa279fb76..083607de5 100644
--- a/examples/boot/application/stm32f3/Cargo.toml
+++ b/examples/boot/application/stm32f3/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml
index 1ec0643a6..74f508515 100644
--- a/examples/boot/application/stm32f7/Cargo.toml
+++ b/examples/boot/application/stm32f7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml
index a4eefe2a5..898b9a47e 100644
--- a/examples/boot/application/stm32h7/Cargo.toml
+++ b/examples/boot/application/stm32h7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml
index 36eada29b..e142c8481 100644
--- a/examples/boot/application/stm32l0/Cargo.toml
+++ b/examples/boot/application/stm32l0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml
index 67efda748..f0e92e1ac 100644
--- a/examples/boot/application/stm32l1/Cargo.toml
+++ b/examples/boot/application/stm32l1/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml
index 4b2e02dd2..87689e9a9 100644
--- a/examples/boot/application/stm32l4/Cargo.toml
+++ b/examples/boot/application/stm32l4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml
index fecbfc51d..a6708bf51 100644
--- a/examples/boot/application/stm32wl/Cargo.toml
+++ b/examples/boot/application/stm32wl/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] }
12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } 12embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" }
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml
index d8c24dfad..7910b372a 100644
--- a/examples/nrf-rtos-trace/Cargo.toml
+++ b/examples/nrf-rtos-trace/Cargo.toml
@@ -17,7 +17,7 @@ log = [
17 17
18[dependencies] 18[dependencies]
19embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } 19embassy-sync = { version = "0.1.0", path = "../../embassy-sync" }
20embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } 20embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] }
21embassy-time = { version = "0.1.0", path = "../../embassy-time" } 21embassy-time = { version = "0.1.0", path = "../../embassy-time" }
22embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 22embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
23 23
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index cc88d92c7..3ece24066 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -12,7 +12,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night
12[dependencies] 12[dependencies]
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
14embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 14embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } 18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml
index e88ddf2f7..4134db46f 100644
--- a/examples/nrf5340/Cargo.toml
+++ b/examples/nrf5340/Cargo.toml
@@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ 9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [
10 "defmt", 10 "defmt",
11] } 11] }
12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ 12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread",
13 "nightly", 13 "nightly",
14 "defmt", 14 "defmt",
15 "integrated-timers", 15 "integrated-timers",
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 1e8870ed7..aea61eec5 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7 7
8[dependencies] 8[dependencies]
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } 12embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 8087df09a..ff08e378c 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] }
11embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } 11embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] }
12embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } 12embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" }
diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml
index 0095a680c..3b1d888f6 100644
--- a/examples/stm32c0/Cargo.toml
+++ b/examples/stm32c0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] }
12 12
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml
index 89d99b6d3..5c82c5579 100644
--- a/examples/stm32f0/Cargo.toml
+++ b/examples/stm32f0/Cargo.toml
@@ -13,7 +13,7 @@ defmt = "0.3"
13defmt-rtt = "0.4" 13defmt-rtt = "0.4"
14panic-probe = "0.3" 14panic-probe = "0.3"
15embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 15embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
16embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 16embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
17embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 17embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
18embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } 18embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] }
19static_cell = "1.0" 19static_cell = "1.0"
diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs
index e0dc8c989..430a805fc 100644
--- a/examples/stm32f0/src/bin/multiprio.rs
+++ b/examples/stm32f0/src/bin/multiprio.rs
@@ -62,7 +62,7 @@ use core::mem;
62use cortex_m::peripheral::NVIC; 62use cortex_m::peripheral::NVIC;
63use cortex_m_rt::entry; 63use cortex_m_rt::entry;
64use defmt::*; 64use defmt::*;
65use embassy_stm32::executor::{Executor, InterruptExecutor}; 65use embassy_executor::{Executor, InterruptExecutor};
66use embassy_stm32::interrupt; 66use embassy_stm32::interrupt;
67use embassy_stm32::pac::Interrupt; 67use embassy_stm32::pac::Interrupt;
68use embassy_time::{Duration, Instant, Timer}; 68use embassy_time::{Duration, Instant, Timer};
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml
index 53f369b3a..387af783a 100644
--- a/examples/stm32f1/Cargo.toml
+++ b/examples/stm32f1/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml
index afaf9a0c9..ffb232310 100644
--- a/examples/stm32f2/Cargo.toml
+++ b/examples/stm32f2/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12 12
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index 69ebef786..38f11201d 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs
index 77df51ac7..5d010f799 100644
--- a/examples/stm32f3/src/bin/multiprio.rs
+++ b/examples/stm32f3/src/bin/multiprio.rs
@@ -62,7 +62,7 @@ use core::mem;
62use cortex_m::peripheral::NVIC; 62use cortex_m::peripheral::NVIC;
63use cortex_m_rt::entry; 63use cortex_m_rt::entry;
64use defmt::*; 64use defmt::*;
65use embassy_stm32::executor::{Executor, InterruptExecutor}; 65use embassy_executor::{Executor, InterruptExecutor};
66use embassy_stm32::interrupt; 66use embassy_stm32::interrupt;
67use embassy_stm32::pac::Interrupt; 67use embassy_stm32::pac::Interrupt;
68use embassy_time::{Duration, Instant, Timer}; 68use embassy_time::{Duration, Instant, Timer};
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 7a7bab5bb..d967d8501 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs
new file mode 100644
index 000000000..2b9ceebc3
--- /dev/null
+++ b/examples/stm32f4/src/bin/mco.rs
@@ -0,0 +1,30 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!");
16
17 let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1);
18 let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4);
19 let mut led = Output::new(p.PB7, Level::High, Speed::Low);
20
21 loop {
22 info!("high");
23 led.set_high();
24 Timer::after(Duration::from_millis(300)).await;
25
26 info!("low");
27 led.set_low();
28 Timer::after(Duration::from_millis(300)).await;
29 }
30}
diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs
index 77df51ac7..5d010f799 100644
--- a/examples/stm32f4/src/bin/multiprio.rs
+++ b/examples/stm32f4/src/bin/multiprio.rs
@@ -62,7 +62,7 @@ use core::mem;
62use cortex_m::peripheral::NVIC; 62use cortex_m::peripheral::NVIC;
63use cortex_m_rt::entry; 63use cortex_m_rt::entry;
64use defmt::*; 64use defmt::*;
65use embassy_stm32::executor::{Executor, InterruptExecutor}; 65use embassy_executor::{Executor, InterruptExecutor};
66use embassy_stm32::interrupt; 66use embassy_stm32::interrupt;
67use embassy_stm32::pac::Interrupt; 67use embassy_stm32::pac::Interrupt;
68use embassy_time::{Duration, Instant, Timer}; 68use embassy_time::{Duration, Instant, Timer};
diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs
index dd171fe13..a93f8baeb 100644
--- a/examples/stm32f4/src/bin/usart_buffered.rs
+++ b/examples/stm32f4/src/bin/usart_buffered.rs
@@ -5,7 +5,7 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::interrupt; 7use embassy_stm32::interrupt;
8use embassy_stm32::usart::{BufferedUart, Config, State}; 8use embassy_stm32::usart::{BufferedUart, Config};
9use embedded_io::asynch::BufRead; 9use embedded_io::asynch::BufRead;
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
@@ -16,20 +16,10 @@ async fn main(_spawner: Spawner) {
16 16
17 let config = Config::default(); 17 let config = Config::default();
18 18
19 let mut state = State::new();
20 let irq = interrupt::take!(USART3); 19 let irq = interrupt::take!(USART3);
21 let mut tx_buf = [0u8; 32]; 20 let mut tx_buf = [0u8; 32];
22 let mut rx_buf = [0u8; 32]; 21 let mut rx_buf = [0u8; 32];
23 let mut buf_usart = BufferedUart::new( 22 let mut buf_usart = BufferedUart::new(p.USART3, irq, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config);
24 &mut state,
25 p.USART3,
26 p.PD9,
27 p.PD8,
28 irq,
29 &mut tx_buf,
30 &mut rx_buf,
31 config,
32 );
33 23
34 loop { 24 loop {
35 let buf = buf_usart.fill_buf().await.unwrap(); 25 let buf = buf_usart.fill_buf().await.unwrap();
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index ea4cbd808..74e7bf53d 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml
index e7273c9fc..03bdbcea3 100644
--- a/examples/stm32g0/Cargo.toml
+++ b/examples/stm32g0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] }
12 12
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 8a57a8ef0..4e4150350 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
12embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } 12embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index a04134789..d0d6a9497 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] }
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 86933a629..413d5c18f 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -10,7 +10,7 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan",
10 10
11[dependencies] 11[dependencies]
12embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
13embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 13embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
14embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 14embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 15embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} 16embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true}
diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs
index 8e84cd092..465347004 100644
--- a/examples/stm32l0/src/bin/usart_irq.rs
+++ b/examples/stm32l0/src/bin/usart_irq.rs
@@ -5,7 +5,7 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::interrupt; 7use embassy_stm32::interrupt;
8use embassy_stm32::usart::{BufferedUart, Config, State}; 8use embassy_stm32::usart::{BufferedUart, Config};
9use embedded_io::asynch::{Read, Write}; 9use embedded_io::asynch::{Read, Write};
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
@@ -20,20 +20,8 @@ async fn main(_spawner: Spawner) {
20 let mut config = Config::default(); 20 let mut config = Config::default();
21 config.baudrate = 9600; 21 config.baudrate = 9600;
22 22
23 let mut state = State::new();
24 let irq = interrupt::take!(USART2); 23 let irq = interrupt::take!(USART2);
25 let mut usart = unsafe { 24 let mut usart = unsafe { BufferedUart::new(p.USART2, irq, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) };
26 BufferedUart::new(
27 &mut state,
28 p.USART2,
29 p.PA3,
30 p.PA2,
31 irq,
32 &mut TX_BUFFER,
33 &mut RX_BUFFER,
34 config,
35 )
36 };
37 25
38 usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); 26 usart.write_all(b"Hello Embassy World!\r\n").await.unwrap();
39 info!("wrote Hello, starting echo"); 27 info!("wrote Hello, starting echo");
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index 6e3b2103c..cd9508d57 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] }
12 12
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index 644c90b1a..7c254eba3 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } 11embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } 12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] }
diff --git a/examples/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs
new file mode 100644
index 000000000..dea0c66e0
--- /dev/null
+++ b/examples/stm32l4/src/bin/mco.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{Mco, Mco1Source, McoClock};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!");
16
17 let _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1);
18
19 let mut led = Output::new(p.PB14, Level::High, Speed::Low);
20
21 loop {
22 led.set_high();
23 Timer::after(Duration::from_millis(300)).await;
24 led.set_low();
25 Timer::after(Duration::from_millis(300)).await;
26 }
27}
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index f880328dc..1c662b9da 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index 2b02eda92..ebef0a4f7 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index e27b4527c..ddf9729e6 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] }
12 12
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 690481bbf..9fc7e0f4a 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] }
12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } 12embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml
index e0e799a34..430d0b4c7 100644
--- a/examples/wasm/Cargo.toml
+++ b/examples/wasm/Cargo.toml
@@ -9,7 +9,7 @@ crate-type = ["cdylib"]
9 9
10[dependencies] 10[dependencies]
11embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 11embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] } 12embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] }
13embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } 13embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] }
14 14
15wasm-logger = "0.2.0" 15wasm-logger = "0.2.0"
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml
index 2a4e8cf41..912749e5d 100644
--- a/tests/nrf/Cargo.toml
+++ b/tests/nrf/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
7[dependencies] 7[dependencies]
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } 9embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } 10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
13embedded-io = { version = "0.4.0", features = ["async"] } 13embedded-io = { version = "0.4.0", features = ["async"] }
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 572a9ce88..eb447be35 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } 11embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] }
12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 12embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 08a775eae..17b640797 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -15,7 +15,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
15 15
16[dependencies] 16[dependencies]
17embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 17embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
18embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 18embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
19embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } 19embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] }
20embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } 20embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] }
21 21