aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/modules/ROOT/nav.adoc1
-rw-r--r--docs/modules/ROOT/pages/bootloader.adoc32
-rw-r--r--embassy-boot/boot/Cargo.toml5
-rw-r--r--embassy-boot/boot/src/lib.rs219
-rw-r--r--embassy-boot/nrf/Cargo.toml2
-rw-r--r--embassy-boot/nrf/src/lib.rs30
-rw-r--r--embassy-boot/stm32/Cargo.toml65
-rw-r--r--embassy-boot/stm32/README.md11
-rw-r--r--embassy-boot/stm32/build.rs27
-rw-r--r--embassy-boot/stm32/memory.x18
-rw-r--r--embassy-boot/stm32/src/fmt.rs225
-rw-r--r--embassy-boot/stm32/src/lib.rs75
-rw-r--r--embassy-boot/stm32/src/main.rs62
-rw-r--r--embassy-stm32/Cargo.toml5
-rw-r--r--embassy-stm32/src/flash/mod.rs401
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/wl.rs156
-rw-r--r--examples/boot/Cargo.toml19
-rw-r--r--examples/boot/nrf/Cargo.toml19
-rw-r--r--examples/boot/nrf/README.md (renamed from examples/boot/README.md)0
-rw-r--r--examples/boot/nrf/build.rs (renamed from examples/boot/build.rs)0
-rw-r--r--examples/boot/nrf/memory.x (renamed from examples/boot/memory.x)0
-rw-r--r--examples/boot/nrf/src/bin/a.rs (renamed from examples/boot/src/bin/a.rs)4
-rw-r--r--examples/boot/nrf/src/bin/b.rs (renamed from examples/boot/src/bin/b.rs)0
-rw-r--r--examples/boot/stm32l0/.cargo/config.toml6
-rw-r--r--examples/boot/stm32l0/Cargo.toml26
-rw-r--r--examples/boot/stm32l0/README.md29
-rw-r--r--examples/boot/stm32l0/build.rs37
-rw-r--r--examples/boot/stm32l0/memory.x15
-rw-r--r--examples/boot/stm32l0/src/bin/a.rs48
-rw-r--r--examples/boot/stm32l0/src/bin/b.rs25
-rw-r--r--examples/boot/stm32l1/.cargo/config.toml6
-rw-r--r--examples/boot/stm32l1/Cargo.toml26
-rw-r--r--examples/boot/stm32l1/README.md29
-rw-r--r--examples/boot/stm32l1/build.rs37
-rw-r--r--examples/boot/stm32l1/memory.x15
-rw-r--r--examples/boot/stm32l1/src/bin/a.rs48
-rw-r--r--examples/boot/stm32l1/src/bin/b.rs25
-rw-r--r--examples/boot/stm32l4/.cargo/config.toml6
-rw-r--r--examples/boot/stm32l4/Cargo.toml26
-rw-r--r--examples/boot/stm32l4/README.md29
-rw-r--r--examples/boot/stm32l4/build.rs37
-rw-r--r--examples/boot/stm32l4/memory.x15
-rw-r--r--examples/boot/stm32l4/src/bin/a.rs44
-rw-r--r--examples/boot/stm32l4/src/bin/b.rs25
-rw-r--r--examples/boot/stm32wl/.cargo/config.toml6
-rw-r--r--examples/boot/stm32wl/Cargo.toml26
-rw-r--r--examples/boot/stm32wl/README.md29
-rw-r--r--examples/boot/stm32wl/build.rs37
-rw-r--r--examples/boot/stm32wl/memory.x15
-rw-r--r--examples/boot/stm32wl/src/bin/a.rs45
-rw-r--r--examples/boot/stm32wl/src/bin/b.rs25
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/src/bin/flash.rs43
-rw-r--r--examples/stm32l1/Cargo.toml1
-rw-r--r--examples/stm32l1/src/bin/flash.rs43
-rw-r--r--examples/stm32wl/Cargo.toml3
-rw-r--r--examples/stm32wl/src/bin/flash.rs43
m---------stm32-data0
59 files changed, 2114 insertions, 136 deletions
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 3aa2eb6bc..a45da1958 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -3,6 +3,7 @@
3* xref:hal.adoc[Hardware Abstraction Layer] 3* xref:hal.adoc[Hardware Abstraction Layer]
4** xref:nrf.adoc[nRF] 4** xref:nrf.adoc[nRF]
5** xref:stm32.adoc[STM32] 5** xref:stm32.adoc[STM32]
6* xref:bootloader.adoc[Bootloader]
6* xref:getting_started.adoc[Getting started] 7* xref:getting_started.adoc[Getting started]
7** xref:basic_application.adoc[Basic application] 8** xref:basic_application.adoc[Basic application]
8** xref:layer_by_layer.adoc[Layer by Layer] 9** xref:layer_by_layer.adoc[Layer by Layer]
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc
new file mode 100644
index 000000000..1a984d6dc
--- /dev/null
+++ b/docs/modules/ROOT/pages/bootloader.adoc
@@ -0,0 +1,32 @@
1= Bootloader
2
3`embassy-boot` a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
4
5The bootloader can be used either as a library or be flashed directly if you are happy with the default configuration and capabilities.
6
7By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
8
9The bootloader supports both internal and external flash by relying on the `embedded-storage` traits.
10
11
12== Hardware support
13
14The bootloader supports
15
16* nRF52 with and without softdevice
17* STM32 L4, WB, WL, L1 and L0
18
19In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work.
20
21== Design
22
23The bootloader divides the storage into 4 main partitions, configured by a linker script:
24
25* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash.
26* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader.
27* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
28* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
29
30The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash, but they have to support compatible page sizes.
31
32The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml
index 0a3006ffc..04deab30b 100644
--- a/embassy-boot/boot/Cargo.toml
+++ b/embassy-boot/boot/Cargo.toml
@@ -21,3 +21,8 @@ log = "0.4"
21env_logger = "0.9" 21env_logger = "0.9"
22rand = "0.8" 22rand = "0.8"
23futures = { version = "0.3", features = ["executor"] } 23futures = { version = "0.3", features = ["executor"] }
24
25[features]
26write-4 = []
27write-8 = []
28invert-erase = []
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 0d33ad1a6..080ea2426 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -17,8 +17,23 @@ mod fmt;
17use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 17use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
18use embedded_storage_async::nor_flash::AsyncNorFlash; 18use embedded_storage_async::nor_flash::AsyncNorFlash;
19 19
20pub const BOOT_MAGIC: u32 = 0xD00DF00D; 20#[cfg(not(any(feature = "write-4", feature = "write-8",)))]
21pub const SWAP_MAGIC: u32 = 0xF00FDAAD; 21compile_error!("No write size/alignment specified. Must specify exactly one of the following features: write-4, write-8");
22
23const BOOT_MAGIC: u8 = 0xD0;
24const SWAP_MAGIC: u8 = 0xF0;
25
26#[cfg(feature = "write-4")]
27const WRITE_SIZE: usize = 4;
28
29#[cfg(feature = "write-8")]
30const WRITE_SIZE: usize = 8;
31
32#[cfg(feature = "invert-erase")]
33const ERASE_VALUE: u8 = 0x00;
34
35#[cfg(not(feature = "invert-erase"))]
36const ERASE_VALUE: u8 = 0xFF;
22 37
23#[derive(Copy, Clone, Debug)] 38#[derive(Copy, Clone, Debug)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))] 39#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -80,12 +95,12 @@ pub trait FlashProvider {
80} 95}
81 96
82/// BootLoader works with any flash implementing embedded_storage and can also work with 97/// BootLoader works with any flash implementing embedded_storage and can also work with
83/// different page sizes. 98/// different page sizes and flash write sizes.
84pub struct BootLoader<const PAGE_SIZE: usize> { 99pub struct BootLoader<const PAGE_SIZE: usize> {
85 // Page with current state of bootloader. The state partition has the following format: 100 // Page with current state of bootloader. The state partition has the following format:
86 // | Range | Description | 101 // | Range | Description |
87 // | 0 - 4 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 102 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
88 // | 4 - N | Progress index used while swapping or reverting | 103 // | WRITE_SIZE - N | Progress index used while swapping or reverting |
89 state: Partition, 104 state: Partition,
90 // Location of the partition which will be booted from 105 // Location of the partition which will be booted from
91 active: Partition, 106 active: Partition,
@@ -100,7 +115,7 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
100 // DFU partition must have an extra page 115 // DFU partition must have an extra page
101 assert!(dfu.len() - active.len() >= PAGE_SIZE); 116 assert!(dfu.len() - active.len() >= PAGE_SIZE);
102 // Ensure we have enough progress pages to store copy progress 117 // Ensure we have enough progress pages to store copy progress
103 assert!(active.len() / PAGE_SIZE >= (state.len() - 4) / PAGE_SIZE); 118 assert!(active.len() / PAGE_SIZE >= (state.len() - WRITE_SIZE) / PAGE_SIZE);
104 Self { active, dfu, state } 119 Self { active, dfu, state }
105 } 120 }
106 121
@@ -203,15 +218,18 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
203 if !self.is_swapped(p.state())? { 218 if !self.is_swapped(p.state())? {
204 trace!("Swapping"); 219 trace!("Swapping");
205 self.swap(p)?; 220 self.swap(p)?;
221 trace!("Swapping done");
206 } else { 222 } else {
207 trace!("Reverting"); 223 trace!("Reverting");
208 self.revert(p)?; 224 self.revert(p)?;
209 225
210 // Overwrite magic and reset progress 226 // Overwrite magic and reset progress
211 let fstate = p.state().flash(); 227 let fstate = p.state().flash();
212 fstate.write(self.state.from as u32, &[0, 0, 0, 0])?; 228 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
229 fstate.write(self.state.from as u32, &aligned.0)?;
213 fstate.erase(self.state.from as u32, self.state.to as u32)?; 230 fstate.erase(self.state.from as u32, self.state.to as u32)?;
214 fstate.write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes())?; 231 let aligned = Aligned([BOOT_MAGIC; WRITE_SIZE]);
232 fstate.write(self.state.from as u32, &aligned.0)?;
215 } 233 }
216 } 234 }
217 _ => {} 235 _ => {}
@@ -227,12 +245,15 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
227 } 245 }
228 246
229 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> { 247 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> {
230 let max_index = ((self.state.len() - 4) / 4) - 1; 248 let max_index = ((self.state.len() - WRITE_SIZE) / WRITE_SIZE) - 1;
231 let flash = p.flash(); 249 let flash = p.flash();
250 let mut aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
232 for i in 0..max_index { 251 for i in 0..max_index {
233 let mut buf: [u8; 4] = [0; 4]; 252 flash.read(
234 flash.read((self.state.from + 4 + i * 4) as u32, &mut buf)?; 253 (self.state.from + WRITE_SIZE + i * WRITE_SIZE) as u32,
235 if buf == [0xFF, 0xFF, 0xFF, 0xFF] { 254 &mut aligned.0,
255 )?;
256 if aligned.0 == [ERASE_VALUE; WRITE_SIZE] {
236 return Ok(i); 257 return Ok(i);
237 } 258 }
238 } 259 }
@@ -241,8 +262,9 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
241 262
242 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> { 263 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> {
243 let flash = p.flash(); 264 let flash = p.flash();
244 let w = self.state.from + 4 + idx * 4; 265 let w = self.state.from + WRITE_SIZE + idx * WRITE_SIZE;
245 flash.write(w as u32, &[0, 0, 0, 0])?; 266 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
267 flash.write(w as u32, &aligned.0)?;
246 Ok(()) 268 Ok(())
247 } 269 }
248 270
@@ -314,21 +336,24 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
314 336
315 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> { 337 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
316 let page_count = self.active.len() / PAGE_SIZE; 338 let page_count = self.active.len() / PAGE_SIZE;
317 // trace!("Page count: {}", page_count); 339 trace!("Page count: {}", page_count);
318 for page in 0..page_count { 340 for page in 0..page_count {
341 trace!("COPY PAGE {}", page);
319 // Copy active page to the 'next' DFU page. 342 // Copy active page to the 'next' DFU page.
320 let active_page = self.active_addr(page_count - 1 - page); 343 let active_page = self.active_addr(page_count - 1 - page);
321 let dfu_page = self.dfu_addr(page_count - page); 344 let dfu_page = self.dfu_addr(page_count - page);
322 info!("Copy active {} to dfu {}", active_page, dfu_page); 345 //trace!("Copy active {} to dfu {}", active_page, dfu_page);
323 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?; 346 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?;
324 347
325 // Copy DFU page to the active page 348 // Copy DFU page to the active page
326 let active_page = self.active_addr(page_count - 1 - page); 349 let active_page = self.active_addr(page_count - 1 - page);
327 let dfu_page = self.dfu_addr(page_count - 1 - page); 350 let dfu_page = self.dfu_addr(page_count - 1 - page);
328 info!("Copy dfy {} to active {}", dfu_page, active_page); 351 //trace!("Copy dfy {} to active {}", dfu_page, active_page);
329 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; 352 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?;
330 } 353 }
331 354
355 info!("DONE COPYING");
356
332 Ok(()) 357 Ok(())
333 } 358 }
334 359
@@ -350,13 +375,15 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
350 } 375 }
351 376
352 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> { 377 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> {
353 let mut magic: [u8; 4] = [0; 4]; 378 let mut magic: [u8; WRITE_SIZE] = [0; WRITE_SIZE];
354 let flash = p.flash(); 379 let flash = p.flash();
355 flash.read(self.state.from as u32, &mut magic)?; 380 flash.read(self.state.from as u32, &mut magic)?;
356 381
357 match u32::from_le_bytes(magic) { 382 info!("Read magic: {:x}", magic);
358 SWAP_MAGIC => Ok(State::Swap), 383 if magic == [SWAP_MAGIC; WRITE_SIZE] {
359 _ => Ok(State::Boot), 384 Ok(State::Swap)
385 } else {
386 Ok(State::Boot)
360 } 387 }
361 } 388 }
362} 389}
@@ -424,6 +451,39 @@ pub struct FirmwareUpdater {
424 dfu: Partition, 451 dfu: Partition,
425} 452}
426 453
454#[cfg(feature = "write-4")]
455#[repr(align(4))]
456pub struct Aligned([u8; 4]);
457
458#[cfg(feature = "write-8")]
459#[repr(align(8))]
460pub struct Aligned([u8; 8]);
461
462impl Default for FirmwareUpdater {
463 fn default() -> Self {
464 extern "C" {
465 static __bootloader_state_start: u32;
466 static __bootloader_state_end: u32;
467 static __bootloader_dfu_start: u32;
468 static __bootloader_dfu_end: u32;
469 }
470
471 let dfu = unsafe {
472 Partition::new(
473 &__bootloader_dfu_start as *const u32 as usize,
474 &__bootloader_dfu_end as *const u32 as usize,
475 )
476 };
477 let state = unsafe {
478 Partition::new(
479 &__bootloader_state_start as *const u32 as usize,
480 &__bootloader_state_end as *const u32 as usize,
481 )
482 };
483 FirmwareUpdater::new(dfu, state)
484 }
485}
486
427impl FirmwareUpdater { 487impl FirmwareUpdater {
428 pub const fn new(dfu: Partition, state: Partition) -> Self { 488 pub const fn new(dfu: Partition, state: Partition) -> Self {
429 Self { dfu, state } 489 Self { dfu, state }
@@ -435,53 +495,45 @@ impl FirmwareUpdater {
435 } 495 }
436 496
437 /// Instruct bootloader that DFU should commence at next boot. 497 /// Instruct bootloader that DFU should commence at next boot.
498 /// Must be provided with an aligned buffer to use for reading and writing magic;
438 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 499 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> {
439 #[repr(align(4))] 500 let mut aligned = Aligned([0; WRITE_SIZE]);
440 struct Aligned([u8; 4]); 501 self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await
441
442 let mut magic = Aligned([0; 4]);
443 flash.read(self.state.from as u32, &mut magic.0).await?;
444 let magic = u32::from_le_bytes(magic.0);
445
446 if magic != SWAP_MAGIC {
447 flash
448 .write(self.state.from as u32, &Aligned([0; 4]).0)
449 .await?;
450 flash
451 .erase(self.state.from as u32, self.state.to as u32)
452 .await?;
453 trace!(
454 "Setting swap magic at {} to 0x{:x}, LE: 0x{:x}",
455 self.state.from,
456 &SWAP_MAGIC,
457 &SWAP_MAGIC.to_le_bytes()
458 );
459 flash
460 .write(self.state.from as u32, &SWAP_MAGIC.to_le_bytes())
461 .await?;
462 }
463 Ok(())
464 } 502 }
465 503
466 /// Mark firmware boot successfully 504 /// Mark firmware boot successfully
467 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 505 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> {
468 #[repr(align(4))] 506 let mut aligned = Aligned([0; WRITE_SIZE]);
469 struct Aligned([u8; 4]); 507 self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await
508 }
470 509
471 let mut magic = Aligned([0; 4]); 510 async fn set_magic<F: AsyncNorFlash>(
472 flash.read(self.state.from as u32, &mut magic.0).await?; 511 &mut self,
473 let magic = u32::from_le_bytes(magic.0); 512 aligned: &mut [u8],
513 magic: u8,
514 flash: &mut F,
515 ) -> Result<(), F::Error> {
516 flash.read(self.state.from as u32, aligned).await?;
474 517
475 if magic != BOOT_MAGIC { 518 let mut is_set = true;
476 flash 519 for b in 0..aligned.len() {
477 .write(self.state.from as u32, &Aligned([0; 4]).0) 520 if aligned[b] != magic {
478 .await?; 521 is_set = false;
522 }
523 }
524 if !is_set {
525 for i in 0..aligned.len() {
526 aligned[i] = 0;
527 }
528 flash.write(self.state.from as u32, aligned).await?;
479 flash 529 flash
480 .erase(self.state.from as u32, self.state.to as u32) 530 .erase(self.state.from as u32, self.state.to as u32)
481 .await?; 531 .await?;
482 flash 532
483 .write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes()) 533 for i in 0..aligned.len() {
484 .await?; 534 aligned[i] = magic;
535 }
536 flash.write(self.state.from as u32, aligned).await?;
485 } 537 }
486 Ok(()) 538 Ok(())
487 } 539 }
@@ -545,6 +597,7 @@ mod tests {
545 use super::*; 597 use super::*;
546 use core::convert::Infallible; 598 use core::convert::Infallible;
547 use core::future::Future; 599 use core::future::Future;
600 use embedded_storage::nor_flash::ErrorType;
548 use embedded_storage_async::nor_flash::AsyncReadNorFlash; 601 use embedded_storage_async::nor_flash::AsyncReadNorFlash;
549 use futures::executor::block_on; 602 use futures::executor::block_on;
550 603
@@ -552,9 +605,11 @@ mod tests {
552 const ACTIVE: Partition = Partition::new(4096, 61440); 605 const ACTIVE: Partition = Partition::new(4096, 61440);
553 const DFU: Partition = Partition::new(61440, 122880); 606 const DFU: Partition = Partition::new(61440, 122880);
554 607
608 /*
555 #[test] 609 #[test]
556 fn test_bad_magic() { 610 fn test_bad_magic() {
557 let mut flash = MemFlash([0xff; 131072]); 611 let mut flash = MemFlash([0xff; 131072]);
612 let mut flash = SingleFlashProvider::new(&mut flash);
558 613
559 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 614 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
560 615
@@ -563,11 +618,13 @@ mod tests {
563 Err(BootError::BadMagic) 618 Err(BootError::BadMagic)
564 ); 619 );
565 } 620 }
621 */
566 622
567 #[test] 623 #[test]
568 fn test_boot_state() { 624 fn test_boot_state() {
569 let mut flash = MemFlash([0xff; 131072]); 625 let mut flash = MemFlash([0xff; 131072]);
570 flash.0[0..4].copy_from_slice(&BOOT_MAGIC.to_le_bytes()); 626 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
627 let mut flash = SingleFlashProvider::new(&mut flash);
571 628
572 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 629 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
573 630
@@ -588,19 +645,19 @@ mod tests {
588 645
589 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 646 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
590 let mut updater = FirmwareUpdater::new(DFU, STATE); 647 let mut updater = FirmwareUpdater::new(DFU, STATE);
591 for i in (DFU.from..DFU.to).step_by(4) { 648 let mut offset = 0;
592 let base = i - DFU.from; 649 for chunk in update.chunks(4096) {
593 let data: [u8; 4] = [ 650 block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap();
594 update[base], 651 offset += chunk.len();
595 update[base + 1],
596 update[base + 2],
597 update[base + 3],
598 ];
599 block_on(updater.write_firmware(i - DFU.from, &data, &mut flash)).unwrap();
600 } 652 }
601 block_on(updater.mark_update(&mut flash)).unwrap(); 653 block_on(updater.mark_update(&mut flash)).unwrap();
602 654
603 assert_eq!(State::Swap, bootloader.prepare_boot(&mut flash).unwrap()); 655 assert_eq!(
656 State::Swap,
657 bootloader
658 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
659 .unwrap()
660 );
604 661
605 for i in ACTIVE.from..ACTIVE.to { 662 for i in ACTIVE.from..ACTIVE.to {
606 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); 663 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i);
@@ -612,7 +669,12 @@ mod tests {
612 } 669 }
613 670
614 // Running again should cause a revert 671 // Running again should cause a revert
615 assert_eq!(State::Swap, bootloader.prepare_boot(&mut flash).unwrap()); 672 assert_eq!(
673 State::Swap,
674 bootloader
675 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
676 .unwrap()
677 );
616 678
617 for i in ACTIVE.from..ACTIVE.to { 679 for i in ACTIVE.from..ACTIVE.to {
618 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); 680 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i);
@@ -625,7 +687,12 @@ mod tests {
625 687
626 // Mark as booted 688 // Mark as booted
627 block_on(updater.mark_booted(&mut flash)).unwrap(); 689 block_on(updater.mark_booted(&mut flash)).unwrap();
628 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 690 assert_eq!(
691 State::Boot,
692 bootloader
693 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
694 .unwrap()
695 );
629 } 696 }
630 697
631 struct MemFlash([u8; 131072]); 698 struct MemFlash([u8; 131072]);
@@ -656,9 +723,12 @@ mod tests {
656 } 723 }
657 } 724 }
658 725
726 impl ErrorType for MemFlash {
727 type Error = Infallible;
728 }
729
659 impl ReadNorFlash for MemFlash { 730 impl ReadNorFlash for MemFlash {
660 const READ_SIZE: usize = 4; 731 const READ_SIZE: usize = 4;
661 type Error = Infallible;
662 732
663 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { 733 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
664 let len = buf.len(); 734 let len = buf.len();
@@ -673,10 +743,9 @@ mod tests {
673 743
674 impl AsyncReadNorFlash for MemFlash { 744 impl AsyncReadNorFlash for MemFlash {
675 const READ_SIZE: usize = 4; 745 const READ_SIZE: usize = 4;
676 type Error = Infallible;
677 746
678 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; 747 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
679 fn read<'a>(&'a mut self, offset: usize, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { 748 fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
680 async move { 749 async move {
681 let len = buf.len(); 750 let len = buf.len();
682 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); 751 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml
index 97207ac29..78157d246 100644
--- a/embassy-boot/nrf/Cargo.toml
+++ b/embassy-boot/nrf/Cargo.toml
@@ -13,7 +13,7 @@ defmt-rtt = { version = "0.3", optional = true }
13 13
14embassy = { path = "../../embassy", default-features = false } 14embassy = { path = "../../embassy", default-features = false }
15embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] } 15embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] }
16embassy-boot = { path = "../boot", default-features = false } 16embassy-boot = { path = "../boot", default-features = false, features = ["write-4"] }
17cortex-m = { version = "0.7" } 17cortex-m = { version = "0.7" }
18cortex-m-rt = { version = "0.7" } 18cortex-m-rt = { version = "0.7" }
19embedded-storage = "0.3.0" 19embedded-storage = "0.3.0"
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index 785cb67e8..500cae500 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -4,9 +4,7 @@
4 4
5mod fmt; 5mod fmt;
6 6
7pub use embassy_boot::{ 7pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider};
8 FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State, BOOT_MAGIC,
9};
10use embassy_nrf::{ 8use embassy_nrf::{
11 nvmc::{Nvmc, PAGE_SIZE}, 9 nvmc::{Nvmc, PAGE_SIZE},
12 peripherals::WDT, 10 peripherals::WDT,
@@ -184,29 +182,3 @@ impl<'d> ReadNorFlash for WatchdogFlash<'d> {
184 self.flash.capacity() 182 self.flash.capacity()
185 } 183 }
186} 184}
187
188pub mod updater {
189 use super::*;
190 pub fn new() -> embassy_boot::FirmwareUpdater {
191 extern "C" {
192 static __bootloader_state_start: u32;
193 static __bootloader_state_end: u32;
194 static __bootloader_dfu_start: u32;
195 static __bootloader_dfu_end: u32;
196 }
197
198 let dfu = unsafe {
199 Partition::new(
200 &__bootloader_dfu_start as *const u32 as usize,
201 &__bootloader_dfu_end as *const u32 as usize,
202 )
203 };
204 let state = unsafe {
205 Partition::new(
206 &__bootloader_state_start as *const u32 as usize,
207 &__bootloader_state_end as *const u32 as usize,
208 )
209 };
210 embassy_boot::FirmwareUpdater::new(dfu, state)
211 }
212}
diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml
new file mode 100644
index 000000000..76bc480bf
--- /dev/null
+++ b/embassy-boot/stm32/Cargo.toml
@@ -0,0 +1,65 @@
1[package]
2authors = [
3 "Ulf Lilleengen <[email protected]>",
4]
5edition = "2018"
6name = "embassy-boot-stm32"
7version = "0.1.0"
8description = "Bootloader for STM32 chips"
9
10[dependencies]
11defmt = { version = "0.3", optional = true }
12defmt-rtt = { version = "0.3", optional = true }
13
14embassy = { path = "../../embassy", default-features = false }
15embassy-stm32 = { path = "../../embassy-stm32", default-features = false, features = ["nightly"] }
16embassy-boot = { path = "../boot", default-features = false }
17cortex-m = { version = "0.7" }
18cortex-m-rt = { version = "0.7" }
19embedded-storage = "0.3.0"
20embedded-storage-async = "0.3.0"
21cfg-if = "1.0.0"
22
23[features]
24defmt = [
25 "dep:defmt",
26 "embassy-boot/defmt",
27 "embassy-stm32/defmt",
28]
29debug = ["defmt-rtt"]
30flash-2k = ["embassy-boot/write-8"]
31flash-128 = ["embassy-boot/write-4"]
32flash-256 = ["embassy-boot/write-4"]
33invert-erase = ["embassy-boot/invert-erase"]
34thumbv6 = []
35
36[profile.dev]
37debug = 2
38debug-assertions = true
39incremental = false
40opt-level = 'z'
41overflow-checks = true
42
43[profile.release]
44codegen-units = 1
45debug = 2
46debug-assertions = false
47incremental = false
48lto = 'fat'
49opt-level = 'z'
50overflow-checks = false
51
52# do not optimize proc-macro crates = faster builds from scratch
53[profile.dev.build-override]
54codegen-units = 8
55debug = false
56debug-assertions = false
57opt-level = 0
58overflow-checks = false
59
60[profile.release.build-override]
61codegen-units = 8
62debug = false
63debug-assertions = false
64opt-level = 0
65overflow-checks = false
diff --git a/embassy-boot/stm32/README.md b/embassy-boot/stm32/README.md
new file mode 100644
index 000000000..a82b730b9
--- /dev/null
+++ b/embassy-boot/stm32/README.md
@@ -0,0 +1,11 @@
1# Bootloader for STM32
2
3The bootloader uses `embassy-boot` to interact with the flash.
4
5# Usage
6
7Flash the bootloader
8
9```
10cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx
11```
diff --git a/embassy-boot/stm32/build.rs b/embassy-boot/stm32/build.rs
new file mode 100644
index 000000000..fd605991f
--- /dev/null
+++ b/embassy-boot/stm32/build.rs
@@ -0,0 +1,27 @@
1use std::env;
2use std::fs::File;
3use std::io::Write;
4use std::path::PathBuf;
5
6fn main() {
7 // Put `memory.x` in our output directory and ensure it's
8 // on the linker search path.
9 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
10 File::create(out.join("memory.x"))
11 .unwrap()
12 .write_all(include_bytes!("memory.x"))
13 .unwrap();
14 println!("cargo:rustc-link-search={}", out.display());
15
16 // By default, Cargo will re-run a build script whenever
17 // any file in the project changes. By specifying `memory.x`
18 // here, we ensure the build script is only re-run when
19 // `memory.x` is changed.
20 println!("cargo:rerun-if-changed=memory.x");
21
22 println!("cargo:rustc-link-arg-bins=--nmagic");
23 println!("cargo:rustc-link-arg-bins=-Tlink.x");
24 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
25 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
26 }
27}
diff --git a/embassy-boot/stm32/memory.x b/embassy-boot/stm32/memory.x
new file mode 100644
index 000000000..c5356ed37
--- /dev/null
+++ b/embassy-boot/stm32/memory.x
@@ -0,0 +1,18 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 ACTIVE : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
13
14__bootloader_active_start = ORIGIN(ACTIVE);
15__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);
16
17__bootloader_dfu_start = ORIGIN(DFU);
18__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);
diff --git a/embassy-boot/stm32/src/fmt.rs b/embassy-boot/stm32/src/fmt.rs
new file mode 100644
index 000000000..066970813
--- /dev/null
+++ b/embassy-boot/stm32/src/fmt.rs
@@ -0,0 +1,225 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
new file mode 100644
index 000000000..d1754a310
--- /dev/null
+++ b/embassy-boot/stm32/src/lib.rs
@@ -0,0 +1,75 @@
1#![no_std]
2#![feature(generic_associated_types)]
3#![feature(type_alias_impl_trait)]
4
5mod fmt;
6
7pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State};
8
9pub struct BootLoader<const PAGE_SIZE: usize> {
10 boot: embassy_boot::BootLoader<PAGE_SIZE>,
11}
12
13impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
14 /// Create a new bootloader instance using parameters from linker script
15 pub fn default() -> Self {
16 extern "C" {
17 static __bootloader_state_start: u32;
18 static __bootloader_state_end: u32;
19 static __bootloader_active_start: u32;
20 static __bootloader_active_end: u32;
21 static __bootloader_dfu_start: u32;
22 static __bootloader_dfu_end: u32;
23 }
24
25 let active = unsafe {
26 Partition::new(
27 &__bootloader_active_start as *const u32 as usize,
28 &__bootloader_active_end as *const u32 as usize,
29 )
30 };
31 let dfu = unsafe {
32 Partition::new(
33 &__bootloader_dfu_start as *const u32 as usize,
34 &__bootloader_dfu_end as *const u32 as usize,
35 )
36 };
37 let state = unsafe {
38 Partition::new(
39 &__bootloader_state_start as *const u32 as usize,
40 &__bootloader_state_end as *const u32 as usize,
41 )
42 };
43
44 trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
45 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
46 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
47
48 Self::new(active, dfu, state)
49 }
50
51 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
52 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
53 Self {
54 boot: embassy_boot::BootLoader::new(active, dfu, state),
55 }
56 }
57
58 /// Boots the application
59 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize {
60 match self.boot.prepare_boot(flash) {
61 Ok(_) => self.boot.boot_address(),
62 Err(_) => panic!("boot prepare error!"),
63 }
64 }
65
66 pub unsafe fn load(&mut self, start: usize) -> ! {
67 trace!("Loading app at 0x{:x}", start);
68 let mut p = cortex_m::Peripherals::steal();
69 #[cfg(not(feature = "thumbv6"))]
70 p.SCB.invalidate_icache();
71 p.SCB.vtor.write(start as u32);
72
73 cortex_m::asm::bootload(start as *const u32)
74 }
75}
diff --git a/embassy-boot/stm32/src/main.rs b/embassy-boot/stm32/src/main.rs
new file mode 100644
index 000000000..6fe0fb66d
--- /dev/null
+++ b/embassy-boot/stm32/src/main.rs
@@ -0,0 +1,62 @@
1#![no_std]
2#![no_main]
3
4use cortex_m_rt::{entry, exception};
5
6#[cfg(feature = "defmt")]
7use defmt_rtt as _;
8
9use embassy_boot_stm32::*;
10use embassy_stm32::flash::Flash;
11
12#[cfg(not(any(feature = "flash-2k", feature = "flash-256", feature = "flash-128")))]
13compile_error!("No flash size specified. Must specify exactly one of the following features: flash-2k, flash-256, flash-128");
14
15#[entry]
16fn main() -> ! {
17 let p = embassy_stm32::init(Default::default());
18
19 // Uncomment this if you are debugging the bootloader with debugger/RTT attached,
20 // as it prevents a hard fault when accessing flash 'too early' after boot.
21 /*
22 for i in 0..10000000 {
23 cortex_m::asm::nop();
24 }
25 */
26
27 #[cfg(feature = "flash-2k")]
28 let mut bl: BootLoader<2048> = BootLoader::default();
29
30 #[cfg(feature = "flash-256")]
31 let mut bl: BootLoader<256> = BootLoader::default();
32
33 #[cfg(feature = "flash-128")]
34 let mut bl: BootLoader<128> = BootLoader::default();
35
36 let mut flash = Flash::unlock(p.FLASH);
37 let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash));
38 core::mem::drop(flash);
39 unsafe { bl.load(start) }
40}
41
42#[no_mangle]
43#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
44unsafe extern "C" fn HardFault() {
45 cortex_m::peripheral::SCB::sys_reset();
46}
47
48#[exception]
49unsafe fn DefaultHandler(_: i16) -> ! {
50 const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
51 let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16;
52
53 panic!("DefaultHandler #{:?}", irqn);
54}
55
56#[panic_handler]
57fn panic(_info: &core::panic::PanicInfo) -> ! {
58 unsafe {
59 cortex_m::asm::udf();
60 core::hint::unreachable_unchecked();
61 }
62}
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 1c9616b71..8152b07d4 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -42,6 +42,9 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un
42embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} 42embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
43embedded-hal-async = { version = "0.1.0-alpha.0", optional = true} 43embedded-hal-async = { version = "0.1.0-alpha.0", optional = true}
44 44
45embedded-storage = "0.3.0"
46embedded-storage-async = { version = "0.3.0", optional = true }
47
45defmt = { version = "0.3", optional = true } 48defmt = { version = "0.3", optional = true }
46log = { version = "0.4.14", optional = true } 49log = { version = "0.4.14", optional = true }
47cortex-m-rt = ">=0.6.15,<0.8" 50cortex-m-rt = ">=0.6.15,<0.8"
@@ -87,7 +90,7 @@ time-driver-tim12 = ["_time-driver"]
87time-driver-tim15 = ["_time-driver"] 90time-driver-tim15 = ["_time-driver"]
88 91
89# Enable nightly-only features 92# Enable nightly-only features
90nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async"] 93nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async"]
91 94
92# Reexport stm32-metapac at `embassy_stm32::pac`. 95# Reexport stm32-metapac at `embassy_stm32::pac`.
93# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. 96# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
new file mode 100644
index 000000000..7a282497f
--- /dev/null
+++ b/embassy-stm32/src/flash/mod.rs
@@ -0,0 +1,401 @@
1use crate::pac;
2use crate::peripherals::FLASH;
3use core::convert::TryInto;
4use core::marker::PhantomData;
5use core::ptr::write_volatile;
6use embassy::util::Unborrow;
7use embassy_hal_common::unborrow;
8
9use embedded_storage::nor_flash::{
10 ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
11};
12
13const FLASH_BASE: usize = 0x8000000;
14
15#[cfg(flash_l4)]
16mod config {
17 use super::*;
18 pub(crate) const FLASH_SIZE: usize = 0x100000;
19 pub(crate) const FLASH_START: usize = FLASH_BASE;
20 pub(crate) const FLASH_END: usize = FLASH_START + FLASH_SIZE;
21 pub(crate) const PAGE_SIZE: usize = 2048;
22 pub(crate) const WORD_SIZE: usize = 8;
23}
24
25#[cfg(flash_wb)]
26mod config {
27 use super::*;
28 pub(crate) const FLASH_SIZE: usize = 0x100000;
29 pub(crate) const FLASH_START: usize = FLASH_BASE;
30 pub(crate) const FLASH_END: usize = FLASH_START + FLASH_SIZE;
31 pub(crate) const PAGE_SIZE: usize = 4096;
32 pub(crate) const WORD_SIZE: usize = 8;
33}
34
35#[cfg(flash_wl)]
36mod config {
37 use super::*;
38 pub(crate) const FLASH_SIZE: usize = 0x40000;
39 pub(crate) const FLASH_START: usize = FLASH_BASE;
40 pub(crate) const FLASH_END: usize = FLASH_START + FLASH_SIZE;
41 pub(crate) const PAGE_SIZE: usize = 2048;
42 pub(crate) const WORD_SIZE: usize = 8;
43}
44
45#[cfg(flash_l0)]
46mod config {
47 use super::*;
48 pub(crate) const FLASH_SIZE: usize = 0x30000;
49 pub(crate) const FLASH_START: usize = FLASH_BASE;
50 pub(crate) const FLASH_END: usize = FLASH_START + FLASH_SIZE;
51 pub(crate) const PAGE_SIZE: usize = 128;
52 pub(crate) const WORD_SIZE: usize = 4;
53}
54
55#[cfg(flash_l1)]
56mod config {
57 use super::*;
58 pub(crate) const FLASH_SIZE: usize = 0x80000;
59 pub(crate) const FLASH_START: usize = FLASH_BASE;
60 pub(crate) const FLASH_END: usize = FLASH_START + FLASH_SIZE;
61 pub(crate) const PAGE_SIZE: usize = 256;
62 pub(crate) const WORD_SIZE: usize = 4;
63}
64
65use config::*;
66
67pub struct Flash<'d> {
68 _inner: FLASH,
69 _phantom: PhantomData<&'d mut FLASH>,
70}
71
72impl<'d> Flash<'d> {
73 pub fn new(p: impl Unborrow<Target = FLASH>) -> Self {
74 unborrow!(p);
75 Self {
76 _inner: p,
77 _phantom: PhantomData,
78 }
79 }
80
81 pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self {
82 let flash = Self::new(p);
83 #[cfg(any(flash_wl, flash_wb, flash_l4))]
84 unsafe {
85 pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
86 pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
87 }
88
89 #[cfg(any(flash_l0))]
90 unsafe {
91 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
92 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
93
94 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
95 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
96 }
97 flash
98 }
99
100 pub fn lock(&mut self) {
101 #[cfg(any(flash_wl, flash_wb, flash_l4))]
102 unsafe {
103 pac::FLASH.cr().modify(|w| w.set_lock(true));
104 }
105
106 #[cfg(any(flash_l0))]
107 unsafe {
108 pac::FLASH.pecr().modify(|w| {
109 w.set_optlock(true);
110 w.set_prglock(true);
111 w.set_pelock(true);
112 });
113 }
114 }
115
116 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
117 if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END {
118 return Err(Error::Size);
119 }
120
121 let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) };
122 bytes.copy_from_slice(flash_data);
123 Ok(())
124 }
125
126 pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> {
127 if offset as usize + buf.len() > FLASH_END {
128 return Err(Error::Size);
129 }
130 if offset as usize % WORD_SIZE != 0 || buf.len() as usize % WORD_SIZE != 0 {
131 return Err(Error::Unaligned);
132 }
133
134 self.clear_all_err();
135
136 #[cfg(any(flash_wl, flash_wb, flash_l4))]
137 unsafe {
138 pac::FLASH.cr().write(|w| w.set_pg(true));
139 }
140
141 let mut ret: Result<(), Error> = Ok(());
142 let mut offset = offset;
143 for chunk in buf.chunks(WORD_SIZE) {
144 for val in chunk.chunks(4) {
145 unsafe {
146 write_volatile(
147 offset as *mut u32,
148 u32::from_le_bytes(val[0..4].try_into().unwrap()),
149 );
150 }
151 offset += val.len() as u32;
152 }
153
154 ret = self.blocking_wait_ready();
155 if ret.is_err() {
156 break;
157 }
158 }
159
160 #[cfg(any(flash_wl, flash_wb, flash_l4))]
161 unsafe {
162 pac::FLASH.cr().write(|w| w.set_pg(false));
163 }
164
165 ret
166 }
167
168 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
169 if to < from || to as usize > FLASH_END {
170 return Err(Error::Size);
171 }
172 if from as usize % PAGE_SIZE != 0 || to as usize % PAGE_SIZE != 0 {
173 return Err(Error::Unaligned);
174 }
175
176 self.clear_all_err();
177
178 for page in (from..to).step_by(PAGE_SIZE) {
179 #[cfg(any(flash_l0, flash_l1))]
180 unsafe {
181 pac::FLASH.pecr().modify(|w| {
182 w.set_erase(true);
183 w.set_prog(true);
184 });
185
186 write_volatile(page as *mut u32, 0xFFFFFFFF);
187 }
188
189 #[cfg(any(flash_wl, flash_wb, flash_l4))]
190 unsafe {
191 let idx = page / PAGE_SIZE as u32;
192
193 pac::FLASH.cr().modify(|w| {
194 w.set_per(true);
195 w.set_pnb(idx as u8);
196 #[cfg(any(flash_wl, flash_wb))]
197 w.set_strt(true);
198 #[cfg(any(flash_l4))]
199 w.set_start(true);
200 });
201 }
202
203 let ret: Result<(), Error> = self.blocking_wait_ready();
204
205 #[cfg(any(flash_wl, flash_wb, flash_l4))]
206 unsafe {
207 pac::FLASH.cr().modify(|w| w.set_per(false));
208 }
209
210 #[cfg(any(flash_l0, flash_l1))]
211 unsafe {
212 pac::FLASH.pecr().modify(|w| {
213 w.set_erase(false);
214 w.set_prog(false);
215 });
216 }
217
218 self.clear_all_err();
219 if ret.is_err() {
220 return ret;
221 }
222 }
223
224 Ok(())
225 }
226
227 fn blocking_wait_ready(&self) -> Result<(), Error> {
228 loop {
229 let sr = unsafe { pac::FLASH.sr().read() };
230
231 if !sr.bsy() {
232 #[cfg(any(flash_wl, flash_wb, flash_l4))]
233 if sr.progerr() {
234 return Err(Error::Prog);
235 }
236
237 if sr.wrperr() {
238 return Err(Error::Protected);
239 }
240
241 if sr.pgaerr() {
242 return Err(Error::Unaligned);
243 }
244
245 if sr.sizerr() {
246 return Err(Error::Size);
247 }
248
249 #[cfg(any(flash_wl, flash_wb, flash_l4))]
250 if sr.miserr() {
251 return Err(Error::Miss);
252 }
253
254 #[cfg(any(flash_wl, flash_wb, flash_l4))]
255 if sr.pgserr() {
256 return Err(Error::Seq);
257 }
258 return Ok(());
259 }
260 }
261 }
262
263 fn clear_all_err(&mut self) {
264 unsafe {
265 pac::FLASH.sr().modify(|w| {
266 #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
267 if w.rderr() {
268 w.set_rderr(false);
269 }
270 #[cfg(any(flash_wl, flash_wb, flash_l4))]
271 if w.fasterr() {
272 w.set_fasterr(false);
273 }
274 #[cfg(any(flash_wl, flash_wb, flash_l4))]
275 if w.miserr() {
276 w.set_miserr(false);
277 }
278 #[cfg(any(flash_wl, flash_wb, flash_l4))]
279 if w.pgserr() {
280 w.set_pgserr(false);
281 }
282 if w.sizerr() {
283 w.set_sizerr(false);
284 }
285 if w.pgaerr() {
286 w.set_pgaerr(false);
287 }
288 if w.wrperr() {
289 w.set_wrperr(false);
290 }
291 #[cfg(any(flash_wl, flash_wb, flash_l4))]
292 if w.progerr() {
293 w.set_progerr(false);
294 }
295 #[cfg(any(flash_wl, flash_wb, flash_l4))]
296 if w.operr() {
297 w.set_operr(false);
298 }
299 });
300 }
301 }
302}
303
304impl Drop for Flash<'_> {
305 fn drop(&mut self) {
306 self.lock();
307 }
308}
309
310#[derive(Debug, Copy, Clone, PartialEq, Eq)]
311#[cfg_attr(feature = "defmt", derive(defmt::Format))]
312pub enum Error {
313 Prog,
314 Size,
315 Miss,
316 Seq,
317 Protected,
318 Unaligned,
319}
320
321impl<'d> ErrorType for Flash<'d> {
322 type Error = Error;
323}
324
325impl NorFlashError for Error {
326 fn kind(&self) -> NorFlashErrorKind {
327 match self {
328 Self::Size => NorFlashErrorKind::OutOfBounds,
329 Self::Unaligned => NorFlashErrorKind::NotAligned,
330 _ => NorFlashErrorKind::Other,
331 }
332 }
333}
334
335impl<'d> ReadNorFlash for Flash<'d> {
336 const READ_SIZE: usize = WORD_SIZE;
337
338 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
339 self.blocking_read(offset, bytes)
340 }
341
342 fn capacity(&self) -> usize {
343 FLASH_SIZE
344 }
345}
346
347impl<'d> NorFlash for Flash<'d> {
348 const WRITE_SIZE: usize = WORD_SIZE;
349 const ERASE_SIZE: usize = PAGE_SIZE;
350
351 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
352 self.blocking_erase(from, to)
353 }
354
355 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
356 self.blocking_write(offset, bytes)
357 }
358}
359
360/*
361cfg_if::cfg_if! {
362 if #[cfg(feature = "nightly")]
363 {
364 use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash};
365 use core::future::Future;
366
367 impl<'d> AsyncNorFlash for Flash<'d> {
368 const WRITE_SIZE: usize = <Self as NorFlash>::WRITE_SIZE;
369 const ERASE_SIZE: usize = <Self as NorFlash>::ERASE_SIZE;
370
371 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
372 fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
373 async move {
374 todo!()
375 }
376 }
377
378 type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
379 fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
380 async move {
381 todo!()
382 }
383 }
384 }
385
386 impl<'d> AsyncReadNorFlash for Flash<'d> {
387 const READ_SIZE: usize = <Self as ReadNorFlash>::READ_SIZE;
388 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
389 fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
390 async move {
391 todo!()
392 }
393 }
394
395 fn capacity(&self) -> usize {
396 FLASH_SIZE
397 }
398 }
399 }
400}
401*/
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 3417c5d9b..ba426128f 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -50,6 +50,8 @@ pub mod i2c;
50 50
51#[cfg(crc)] 51#[cfg(crc)]
52pub mod crc; 52pub mod crc;
53#[cfg(any(flash_l0, flash_l1, flash_wl, flash_wb, flash_l4))]
54pub mod flash;
53pub mod pwm; 55pub mod pwm;
54#[cfg(rng)] 56#[cfg(rng)]
55pub mod rng; 57pub mod rng;
diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs
index fb2dd9986..1f2fbaf87 100644
--- a/embassy-stm32/src/rcc/wl.rs
+++ b/embassy-stm32/src/rcc/wl.rs
@@ -1,4 +1,4 @@
1use crate::pac::RCC; 1use crate::pac::{FLASH, RCC};
2use crate::rcc::{set_freqs, Clocks}; 2use crate::rcc::{set_freqs, Clocks};
3use crate::time::U32Ext; 3use crate::time::U32Ext;
4 4
@@ -15,10 +15,101 @@ pub const HSE32_FREQ: u32 = 32_000_000;
15/// System clock mux source 15/// System clock mux source
16#[derive(Clone, Copy)] 16#[derive(Clone, Copy)]
17pub enum ClockSrc { 17pub enum ClockSrc {
18 MSI(MSIRange),
18 HSE32, 19 HSE32,
19 HSI16, 20 HSI16,
20} 21}
21 22
23#[derive(Clone, Copy, PartialOrd, PartialEq)]
24pub enum MSIRange {
25 /// Around 100 kHz
26 Range0,
27 /// Around 200 kHz
28 Range1,
29 /// Around 400 kHz
30 Range2,
31 /// Around 800 kHz
32 Range3,
33 /// Around 1 MHz
34 Range4,
35 /// Around 2 MHz
36 Range5,
37 /// Around 4 MHz (reset value)
38 Range6,
39 /// Around 8 MHz
40 Range7,
41 /// Around 16 MHz
42 Range8,
43 /// Around 24 MHz
44 Range9,
45 /// Around 32 MHz
46 Range10,
47 /// Around 48 MHz
48 Range11,
49}
50
51impl MSIRange {
52 fn freq(&self) -> u32 {
53 match self {
54 MSIRange::Range0 => 100_000,
55 MSIRange::Range1 => 200_000,
56 MSIRange::Range2 => 400_000,
57 MSIRange::Range3 => 800_000,
58 MSIRange::Range4 => 1_000_000,
59 MSIRange::Range5 => 2_000_000,
60 MSIRange::Range6 => 4_000_000,
61 MSIRange::Range7 => 8_000_000,
62 MSIRange::Range8 => 16_000_000,
63 MSIRange::Range9 => 24_000_000,
64 MSIRange::Range10 => 32_000_000,
65 MSIRange::Range11 => 48_000_000,
66 }
67 }
68
69 fn vos(&self) -> VoltageScale {
70 if self > &MSIRange::Range8 {
71 VoltageScale::Range1
72 } else {
73 VoltageScale::Range2
74 }
75 }
76}
77
78impl Default for MSIRange {
79 fn default() -> MSIRange {
80 MSIRange::Range6
81 }
82}
83
84impl Into<u8> for MSIRange {
85 fn into(self) -> u8 {
86 match self {
87 MSIRange::Range0 => 0b0000,
88 MSIRange::Range1 => 0b0001,
89 MSIRange::Range2 => 0b0010,
90 MSIRange::Range3 => 0b0011,
91 MSIRange::Range4 => 0b0100,
92 MSIRange::Range5 => 0b0101,
93 MSIRange::Range6 => 0b0110,
94 MSIRange::Range7 => 0b0111,
95 MSIRange::Range8 => 0b1000,
96 MSIRange::Range9 => 0b1001,
97 MSIRange::Range10 => 0b1010,
98 MSIRange::Range11 => 0b1011,
99 }
100 }
101}
102
103/// Voltage Scale
104///
105/// Represents the voltage range feeding the CPU core. The maximum core
106/// clock frequency depends on this value.
107#[derive(Copy, Clone, PartialEq)]
108pub enum VoltageScale {
109 Range1,
110 Range2,
111}
112
22/// AHB prescaler 113/// AHB prescaler
23#[derive(Clone, Copy, PartialEq)] 114#[derive(Clone, Copy, PartialEq)]
24pub enum AHBPrescaler { 115pub enum AHBPrescaler {
@@ -85,6 +176,7 @@ impl Into<u8> for AHBPrescaler {
85pub struct Config { 176pub struct Config {
86 pub mux: ClockSrc, 177 pub mux: ClockSrc,
87 pub ahb_pre: AHBPrescaler, 178 pub ahb_pre: AHBPrescaler,
179 pub shd_ahb_pre: AHBPrescaler,
88 pub apb1_pre: APBPrescaler, 180 pub apb1_pre: APBPrescaler,
89 pub apb2_pre: APBPrescaler, 181 pub apb2_pre: APBPrescaler,
90 pub enable_lsi: bool, 182 pub enable_lsi: bool,
@@ -94,8 +186,9 @@ impl Default for Config {
94 #[inline] 186 #[inline]
95 fn default() -> Config { 187 fn default() -> Config {
96 Config { 188 Config {
97 mux: ClockSrc::HSI16, 189 mux: ClockSrc::MSI(MSIRange::default()),
98 ahb_pre: AHBPrescaler::NotDivided, 190 ahb_pre: AHBPrescaler::NotDivided,
191 shd_ahb_pre: AHBPrescaler::NotDivided,
99 apb1_pre: APBPrescaler::NotDivided, 192 apb1_pre: APBPrescaler::NotDivided,
100 apb2_pre: APBPrescaler::NotDivided, 193 apb2_pre: APBPrescaler::NotDivided,
101 enable_lsi: false, 194 enable_lsi: false,
@@ -104,13 +197,13 @@ impl Default for Config {
104} 197}
105 198
106pub(crate) unsafe fn init(config: Config) { 199pub(crate) unsafe fn init(config: Config) {
107 let (sys_clk, sw) = match config.mux { 200 let (sys_clk, sw, vos) = match config.mux {
108 ClockSrc::HSI16 => { 201 ClockSrc::HSI16 => {
109 // Enable HSI16 202 // Enable HSI16
110 RCC.cr().write(|w| w.set_hsion(true)); 203 RCC.cr().write(|w| w.set_hsion(true));
111 while !RCC.cr().read().hsirdy() {} 204 while !RCC.cr().read().hsirdy() {}
112 205
113 (HSI_FREQ, 0x01) 206 (HSI_FREQ, 0x01, VoltageScale::Range2)
114 } 207 }
115 ClockSrc::HSE32 => { 208 ClockSrc::HSE32 => {
116 // Enable HSE32 209 // Enable HSE32
@@ -120,7 +213,17 @@ pub(crate) unsafe fn init(config: Config) {
120 }); 213 });
121 while !RCC.cr().read().hserdy() {} 214 while !RCC.cr().read().hserdy() {}
122 215
123 (HSE32_FREQ, 0x02) 216 (HSE32_FREQ, 0x02, VoltageScale::Range1)
217 }
218 ClockSrc::MSI(range) => {
219 RCC.cr().write(|w| {
220 w.set_msirange(range.into());
221 w.set_msion(true);
222 });
223
224 while !RCC.cr().read().msirdy() {}
225
226 (range.freq(), 0x00, range.vos())
124 } 227 }
125 }; 228 };
126 229
@@ -135,6 +238,14 @@ pub(crate) unsafe fn init(config: Config) {
135 w.set_ppre2(config.apb2_pre.into()); 238 w.set_ppre2(config.apb2_pre.into());
136 }); 239 });
137 240
241 RCC.extcfgr().modify(|w| {
242 if config.shd_ahb_pre == AHBPrescaler::NotDivided {
243 w.set_shdhpre(0);
244 } else {
245 w.set_shdhpre(config.shd_ahb_pre.into());
246 }
247 });
248
138 let ahb_freq: u32 = match config.ahb_pre { 249 let ahb_freq: u32 = match config.ahb_pre {
139 AHBPrescaler::NotDivided => sys_clk, 250 AHBPrescaler::NotDivided => sys_clk,
140 pre => { 251 pre => {
@@ -144,6 +255,15 @@ pub(crate) unsafe fn init(config: Config) {
144 } 255 }
145 }; 256 };
146 257
258 let shd_ahb_freq: u32 = match config.shd_ahb_pre {
259 AHBPrescaler::NotDivided => sys_clk,
260 pre => {
261 let pre: u8 = pre.into();
262 let pre = 1 << (pre as u32 - 7);
263 sys_clk / pre
264 }
265 };
266
147 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { 267 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
148 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 268 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
149 pre => { 269 pre => {
@@ -164,8 +284,7 @@ pub(crate) unsafe fn init(config: Config) {
164 } 284 }
165 }; 285 };
166 286
167 // TODO: completely untested 287 let apb3_freq = shd_ahb_freq;
168 let apb3_freq = ahb_freq;
169 288
170 if config.enable_lsi { 289 if config.enable_lsi {
171 let csr = RCC.csr().read(); 290 let csr = RCC.csr().read();
@@ -175,11 +294,32 @@ pub(crate) unsafe fn init(config: Config) {
175 } 294 }
176 } 295 }
177 296
297 // Adjust flash latency
298 let flash_clk_src_freq: u32 = shd_ahb_freq;
299 let ws = match vos {
300 VoltageScale::Range1 => match flash_clk_src_freq {
301 0..=18_000_000 => 0b000,
302 18_000_001..=36_000_000 => 0b001,
303 _ => 0b010,
304 },
305 VoltageScale::Range2 => match flash_clk_src_freq {
306 0..=6_000_000 => 0b000,
307 6_000_001..=12_000_000 => 0b001,
308 _ => 0b010,
309 },
310 };
311
312 FLASH.acr().modify(|w| {
313 w.set_latency(ws);
314 });
315
316 while FLASH.acr().read().latency() != ws {}
317
178 set_freqs(Clocks { 318 set_freqs(Clocks {
179 sys: sys_clk.hz(), 319 sys: sys_clk.hz(),
180 ahb1: ahb_freq.hz(), 320 ahb1: ahb_freq.hz(),
181 ahb2: ahb_freq.hz(), 321 ahb2: ahb_freq.hz(),
182 ahb3: ahb_freq.hz(), 322 ahb3: shd_ahb_freq.hz(),
183 apb1: apb1_freq.hz(), 323 apb1: apb1_freq.hz(),
184 apb2: apb2_freq.hz(), 324 apb2: apb2_freq.hz(),
185 apb3: apb3_freq.hz(), 325 apb3: apb3_freq.hz(),
diff --git a/examples/boot/Cargo.toml b/examples/boot/Cargo.toml
deleted file mode 100644
index 2da659478..000000000
--- a/examples/boot/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../embassy", features = ["nightly"] }
9embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
10embassy-boot-nrf = { version = "0.1.0", path = "../../embassy-boot/nrf" }
11embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
diff --git a/examples/boot/nrf/Cargo.toml b/examples/boot/nrf/Cargo.toml
new file mode 100644
index 000000000..0a5bb8f9d
--- /dev/null
+++ b/examples/boot/nrf/Cargo.toml
@@ -0,0 +1,19 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-nrf-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-nrf = { version = "0.1.0", path = "../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
10embassy-boot-nrf = { version = "0.1.0", path = "../../../embassy-boot/nrf" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
diff --git a/examples/boot/README.md b/examples/boot/nrf/README.md
index b97513a9d..b97513a9d 100644
--- a/examples/boot/README.md
+++ b/examples/boot/nrf/README.md
diff --git a/examples/boot/build.rs b/examples/boot/nrf/build.rs
index cd1a264c4..cd1a264c4 100644
--- a/examples/boot/build.rs
+++ b/examples/boot/nrf/build.rs
diff --git a/examples/boot/memory.x b/examples/boot/nrf/memory.x
index dfb72103f..dfb72103f 100644
--- a/examples/boot/memory.x
+++ b/examples/boot/nrf/memory.x
diff --git a/examples/boot/src/bin/a.rs b/examples/boot/nrf/src/bin/a.rs
index d18b508cc..caf8140d8 100644
--- a/examples/boot/src/bin/a.rs
+++ b/examples/boot/nrf/src/bin/a.rs
@@ -4,7 +4,7 @@
4#![feature(generic_associated_types)] 4#![feature(generic_associated_types)]
5#![feature(type_alias_impl_trait)] 5#![feature(type_alias_impl_trait)]
6 6
7use embassy_boot_nrf::updater; 7use embassy_boot_nrf::FirmwareUpdater;
8use embassy_nrf::{ 8use embassy_nrf::{
9 gpio::{Input, Pull}, 9 gpio::{Input, Pull},
10 gpio::{Level, Output, OutputDrive}, 10 gpio::{Level, Output, OutputDrive},
@@ -26,10 +26,10 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
26 let nvmc = Nvmc::new(p.NVMC); 26 let nvmc = Nvmc::new(p.NVMC);
27 let mut nvmc = BlockingAsync::new(nvmc); 27 let mut nvmc = BlockingAsync::new(nvmc);
28 28
29 let mut updater = FirmwareUpdater::default();
29 loop { 30 loop {
30 button.wait_for_any_edge().await; 31 button.wait_for_any_edge().await;
31 if button.is_low() { 32 if button.is_low() {
32 let mut updater = updater::new();
33 let mut offset = 0; 33 let mut offset = 0;
34 for chunk in APP_B.chunks(4096) { 34 for chunk in APP_B.chunks(4096) {
35 let mut buf: [u8; 4096] = [0; 4096]; 35 let mut buf: [u8; 4096] = [0; 4096];
diff --git a/examples/boot/src/bin/b.rs b/examples/boot/nrf/src/bin/b.rs
index 18bb6330c..18bb6330c 100644
--- a/examples/boot/src/bin/b.rs
+++ b/examples/boot/nrf/src/bin/b.rs
diff --git a/examples/boot/stm32l0/.cargo/config.toml b/examples/boot/stm32l0/.cargo/config.toml
new file mode 100644
index 000000000..840faa62e
--- /dev/null
+++ b/examples/boot/stm32l0/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L072CZTx"
4
5[build]
6target = "thumbv6m-none-eabi"
diff --git a/examples/boot/stm32l0/Cargo.toml b/examples/boot/stm32l0/Cargo.toml
new file mode 100644
index 000000000..2e093d771
--- /dev/null
+++ b/examples/boot/stm32l0/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32l0-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-128", "invert-erase", "thumbv6"] }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32l0/README.md b/examples/boot/stm32l0/README.md
new file mode 100644
index 000000000..9c8660821
--- /dev/null
+++ b/examples/boot/stm32l0/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32L0 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l072cz,flash-128,invert-erase,thumbv6 --chip STM32L072CZTx
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32L072CZTx
29```
diff --git a/examples/boot/stm32l0/build.rs b/examples/boot/stm32l0/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32l0/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32l0/memory.x b/examples/boot/stm32l0/memory.x
new file mode 100644
index 000000000..fd5bf1a5d
--- /dev/null
+++ b/examples/boot/stm32l0/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
13
14__bootloader_dfu_start = ORIGIN(DFU);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);
diff --git a/examples/boot/stm32l0/src/bin/a.rs b/examples/boot/stm32l0/src/bin/a.rs
new file mode 100644
index 000000000..7b9000c91
--- /dev/null
+++ b/examples/boot/stm32l0/src/bin/a.rs
@@ -0,0 +1,48 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::time::{Duration, Timer};
6use embassy_boot_stm32::FirmwareUpdater;
7use embassy_stm32::exti::ExtiInput;
8use embassy_stm32::flash::Flash;
9use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
10use embassy_stm32::Peripherals;
11use embassy_traits::adapter::BlockingAsync;
12use panic_reset as _;
13
14#[cfg(feature = "defmt-rtt")]
15use defmt_rtt::*;
16
17static APP_B: &[u8] = include_bytes!("../../b.bin");
18
19#[embassy::main]
20async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
21 let flash = Flash::unlock(p.FLASH);
22 let mut flash = BlockingAsync::new(flash);
23
24 let button = Input::new(p.PB2, Pull::Up);
25 let mut button = ExtiInput::new(button, p.EXTI2);
26
27 let mut led = Output::new(p.PB5, Level::Low, Speed::Low);
28
29 led.set_high();
30
31 let mut updater = FirmwareUpdater::default();
32 button.wait_for_falling_edge().await;
33 let mut offset = 0;
34 for chunk in APP_B.chunks(128) {
35 let mut buf: [u8; 128] = [0; 128];
36 buf[..chunk.len()].copy_from_slice(chunk);
37 updater
38 .write_firmware(offset, &buf, &mut flash, 128)
39 .await
40 .unwrap();
41 offset += chunk.len();
42 }
43
44 updater.mark_update(&mut flash).await.unwrap();
45 led.set_low();
46 Timer::after(Duration::from_secs(1)).await;
47 cortex_m::peripheral::SCB::sys_reset();
48}
diff --git a/examples/boot/stm32l0/src/bin/b.rs b/examples/boot/stm32l0/src/bin/b.rs
new file mode 100644
index 000000000..ed774fd70
--- /dev/null
+++ b/examples/boot/stm32l0/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PB6, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32l1/.cargo/config.toml b/examples/boot/stm32l1/.cargo/config.toml
new file mode 100644
index 000000000..04985720b
--- /dev/null
+++ b/examples/boot/stm32l1/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L151CBxxA"
4
5[build]
6target = "thumbv7m-none-eabi"
diff --git a/examples/boot/stm32l1/Cargo.toml b/examples/boot/stm32l1/Cargo.toml
new file mode 100644
index 000000000..ec396bef2
--- /dev/null
+++ b/examples/boot/stm32l1/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32l1-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-256", "invert-erase"] }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32l1/README.md b/examples/boot/stm32l1/README.md
new file mode 100644
index 000000000..1a9e85a75
--- /dev/null
+++ b/examples/boot/stm32l1/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32L1 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l151cb-a,flash-256,invert-erase --chip STM32L151CBxxA
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32L151CBxxA
29```
diff --git a/examples/boot/stm32l1/build.rs b/examples/boot/stm32l1/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32l1/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32l1/memory.x b/examples/boot/stm32l1/memory.x
new file mode 100644
index 000000000..fd5bf1a5d
--- /dev/null
+++ b/examples/boot/stm32l1/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
13
14__bootloader_dfu_start = ORIGIN(DFU);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);
diff --git a/examples/boot/stm32l1/src/bin/a.rs b/examples/boot/stm32l1/src/bin/a.rs
new file mode 100644
index 000000000..7b9000c91
--- /dev/null
+++ b/examples/boot/stm32l1/src/bin/a.rs
@@ -0,0 +1,48 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::time::{Duration, Timer};
6use embassy_boot_stm32::FirmwareUpdater;
7use embassy_stm32::exti::ExtiInput;
8use embassy_stm32::flash::Flash;
9use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
10use embassy_stm32::Peripherals;
11use embassy_traits::adapter::BlockingAsync;
12use panic_reset as _;
13
14#[cfg(feature = "defmt-rtt")]
15use defmt_rtt::*;
16
17static APP_B: &[u8] = include_bytes!("../../b.bin");
18
19#[embassy::main]
20async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
21 let flash = Flash::unlock(p.FLASH);
22 let mut flash = BlockingAsync::new(flash);
23
24 let button = Input::new(p.PB2, Pull::Up);
25 let mut button = ExtiInput::new(button, p.EXTI2);
26
27 let mut led = Output::new(p.PB5, Level::Low, Speed::Low);
28
29 led.set_high();
30
31 let mut updater = FirmwareUpdater::default();
32 button.wait_for_falling_edge().await;
33 let mut offset = 0;
34 for chunk in APP_B.chunks(128) {
35 let mut buf: [u8; 128] = [0; 128];
36 buf[..chunk.len()].copy_from_slice(chunk);
37 updater
38 .write_firmware(offset, &buf, &mut flash, 128)
39 .await
40 .unwrap();
41 offset += chunk.len();
42 }
43
44 updater.mark_update(&mut flash).await.unwrap();
45 led.set_low();
46 Timer::after(Duration::from_secs(1)).await;
47 cortex_m::peripheral::SCB::sys_reset();
48}
diff --git a/examples/boot/stm32l1/src/bin/b.rs b/examples/boot/stm32l1/src/bin/b.rs
new file mode 100644
index 000000000..ed774fd70
--- /dev/null
+++ b/examples/boot/stm32l1/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PB6, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32l4/.cargo/config.toml b/examples/boot/stm32l4/.cargo/config.toml
new file mode 100644
index 000000000..7b6c4c0ac
--- /dev/null
+++ b/examples/boot/stm32l4/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L475VG"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32l4/Cargo.toml b/examples/boot/stm32l4/Cargo.toml
new file mode 100644
index 000000000..394f26a12
--- /dev/null
+++ b/examples/boot/stm32l4/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32l4-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-2k"] }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32l4/README.md b/examples/boot/stm32l4/README.md
new file mode 100644
index 000000000..09e09d6ef
--- /dev/null
+++ b/examples/boot/stm32l4/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32L4 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l475vg,flash-2k --chip STM32L475VG
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32L475VG
29```
diff --git a/examples/boot/stm32l4/build.rs b/examples/boot/stm32l4/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32l4/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32l4/memory.x b/examples/boot/stm32l4/memory.x
new file mode 100644
index 000000000..fd5bf1a5d
--- /dev/null
+++ b/examples/boot/stm32l4/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
13
14__bootloader_dfu_start = ORIGIN(DFU);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);
diff --git a/examples/boot/stm32l4/src/bin/a.rs b/examples/boot/stm32l4/src/bin/a.rs
new file mode 100644
index 000000000..a5a9e2302
--- /dev/null
+++ b/examples/boot/stm32l4/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_falling_edge().await;
31 let mut offset = 0;
32 for chunk in APP_B.chunks(2048) {
33 let mut buf: [u8; 2048] = [0; 2048];
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.mark_update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32l4/src/bin/b.rs b/examples/boot/stm32l4/src/bin/b.rs
new file mode 100644
index 000000000..814275988
--- /dev/null
+++ b/examples/boot/stm32l4/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PA5, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32wl/.cargo/config.toml b/examples/boot/stm32wl/.cargo/config.toml
new file mode 100644
index 000000000..60076e06b
--- /dev/null
+++ b/examples/boot/stm32wl/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32WLE5JCIx"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32wl/Cargo.toml b/examples/boot/stm32wl/Cargo.toml
new file mode 100644
index 000000000..9c69f4a65
--- /dev/null
+++ b/examples/boot/stm32wl/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32wl-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-2k"] }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32wl/README.md b/examples/boot/stm32wl/README.md
new file mode 100644
index 000000000..a26a23852
--- /dev/null
+++ b/examples/boot/stm32wl/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32wl55jc-cm4,flash-2k --chip STM32WLE5JCIx
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32WLE5JCIx
29```
diff --git a/examples/boot/stm32wl/build.rs b/examples/boot/stm32wl/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32wl/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32wl/memory.x b/examples/boot/stm32wl/memory.x
new file mode 100644
index 000000000..78dd69c35
--- /dev/null
+++ b/examples/boot/stm32wl/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
13
14__bootloader_dfu_start = ORIGIN(DFU);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);
diff --git a/examples/boot/stm32wl/src/bin/a.rs b/examples/boot/stm32wl/src/bin/a.rs
new file mode 100644
index 000000000..b1f4a4a03
--- /dev/null
+++ b/examples/boot/stm32wl/src/bin/a.rs
@@ -0,0 +1,45 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::new(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PA0, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI0);
25
26 let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
27
28 let mut updater = FirmwareUpdater::default();
29 button.wait_for_falling_edge().await;
30 let mut offset = 0;
31 for chunk in APP_B.chunks(2048) {
32 let mut buf: [u8; 2048] = [0; 2048];
33 buf[..chunk.len()].copy_from_slice(chunk);
34 // defmt::info!("Writing chunk at 0x{:x}", offset);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.mark_update(&mut flash).await.unwrap();
42 // defmt::info!("Marked as updated");
43 led.set_high();
44 cortex_m::peripheral::SCB::sys_reset();
45}
diff --git a/examples/boot/stm32wl/src/bin/b.rs b/examples/boot/stm32wl/src/bin/b.rs
new file mode 100644
index 000000000..ffe15b661
--- /dev/null
+++ b/examples/boot/stm32wl/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PB15, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 0b21f6742..1cdb2f374 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -21,6 +21,8 @@ lorawan = { version = "0.7.1", default-features = false, features = ["default-cr
21defmt = "0.3" 21defmt = "0.3"
22defmt-rtt = "0.3" 22defmt-rtt = "0.3"
23 23
24embedded-storage = "0.3.0"
25
24cortex-m = "0.7.3" 26cortex-m = "0.7.3"
25cortex-m-rt = "0.7.0" 27cortex-m-rt = "0.7.0"
26panic-probe = { version = "0.3", features = ["print-defmt"] } 28panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs
new file mode 100644
index 000000000..0ab7b133b
--- /dev/null
+++ b/examples/stm32l0/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x8026000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 128));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read after erase: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index a7a6c228e..ce6b07729 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -18,3 +18,4 @@ embedded-hal = "0.2.6"
18panic-probe = { version = "0.3", features = ["print-defmt"] } 18panic-probe = { version = "0.3", features = ["print-defmt"] }
19futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 19futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
20heapless = { version = "0.7.5", default-features = false } 20heapless = { version = "0.7.5", default-features = false }
21embedded-storage = "0.3.0"
diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs
new file mode 100644
index 000000000..b234289af
--- /dev/null
+++ b/examples/stm32l1/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x8026000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 256));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read after erase: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 919104b01..01f317039 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -8,7 +8,7 @@ resolver = "2"
8[dependencies] 8[dependencies]
9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } 10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] }
11embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time"] } 11embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
12 12
13lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } 13lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] }
14lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } 14lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] }
@@ -19,6 +19,7 @@ defmt-rtt = "0.3"
19cortex-m = "0.7.3" 19cortex-m = "0.7.3"
20cortex-m-rt = "0.7.0" 20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22embedded-storage = "0.3.0"
22panic-probe = { version = "0.3", features = ["print-defmt"] } 23panic-probe = { version = "0.3", features = ["print-defmt"] }
23futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 24futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
24heapless = { version = "0.7.5", default-features = false } 25heapless = { version = "0.7.5", default-features = false }
diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs
new file mode 100644
index 000000000..9e13c702a
--- /dev/null
+++ b/examples/stm32wl/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x8036000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 2048));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/stm32-data b/stm32-data
Subproject 472ee98e8fdb11312392e47b16568c9d02fe654 Subproject 419701c835dd0da3c37d8de02c95115f500dfa6