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