summaryrefslogtreecommitdiff
path: root/examples/boot
diff options
context:
space:
mode:
authorUlf Lilleengen <lulf@redhat.com>2022-04-20 13:49:59 +0200
committerUlf Lilleengen <ulf.lilleengen@gmail.com>2022-04-27 15:17:18 +0200
commit484e0acc638c27366e19275c32db9c8487ea8fba (patch)
tree5649591dad34cadcb28503f94c1bbca0bf5578a1 /examples/boot
parent9c283cd44504d6d9d6f9e352e4c7a8d043bd673f (diff)
downloadembassy-484e0acc638c27366e19275c32db9c8487ea8fba.zip
Add stm32 flash + bootloader support
* Add flash drivers for L0, L1, L4, WB and WL. Not tested for WB, but should be similar to WL. * Add embassy-boot-stm32 for bootloading on STM32. * Add flash examples and bootloader examples * Update stm32-data
Diffstat (limited to 'examples/boot')
-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
35 files changed, 758 insertions, 21 deletions
diff --git a/examples/boot/Cargo.toml b/examples/boot/Cargo.toml
deleted file mode 100644
index 2da65947..00000000
--- a/examples/boot/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
-[package]
-authors = ["Ulf Lilleengen <lulf@redhat.com>"]
-edition = "2018"
-name = "embassy-boot-examples"
-version = "0.1.0"
-
-[dependencies]
-embassy = { version = "0.1.0", path = "../../embassy", features = ["nightly"] }
-embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
-embassy-boot-nrf = { version = "0.1.0", path = "../../embassy-boot/nrf" }
-embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
-
-defmt = { version = "0.3", optional = true }
-defmt-rtt = { version = "0.3", optional = true }
-panic-reset = { version = "0.1.1" }
-embedded-hal = { version = "0.2.6" }
-
-cortex-m = "0.7.3"
-cortex-m-rt = "0.7.0"
diff --git a/examples/boot/nrf/Cargo.toml b/examples/boot/nrf/Cargo.toml
new file mode 100644
index 00000000..0a5bb8f9
--- /dev/null
+++ b/examples/boot/nrf/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+authors = ["Ulf Lilleengen <lulf@redhat.com>"]
+edition = "2018"
+name = "embassy-boot-nrf-examples"
+version = "0.1.0"
+
+[dependencies]
+embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
+embassy-nrf = { version = "0.1.0", path = "../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
+embassy-boot-nrf = { version = "0.1.0", path = "../../../embassy-boot/nrf" }
+embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
+
+defmt = { version = "0.3", optional = true }
+defmt-rtt = { version = "0.3", optional = true }
+panic-reset = { version = "0.1.1" }
+embedded-hal = { version = "0.2.6" }
+
+cortex-m = "0.7.3"
+cortex-m-rt = "0.7.0"
diff --git a/examples/boot/README.md b/examples/boot/nrf/README.md
index b97513a9..b97513a9 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 cd1a264c..cd1a264c 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 dfb72103..dfb72103 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 d18b508c..caf8140d 100644
--- a/examples/boot/src/bin/a.rs
+++ b/examples/boot/nrf/src/bin/a.rs
@@ -4,7 +4,7 @@
#![feature(generic_associated_types)]
#![feature(type_alias_impl_trait)]
-use embassy_boot_nrf::updater;
+use embassy_boot_nrf::FirmwareUpdater;
use embassy_nrf::{
gpio::{Input, Pull},
gpio::{Level, Output, OutputDrive},
@@ -26,10 +26,10 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
let nvmc = Nvmc::new(p.NVMC);
let mut nvmc = BlockingAsync::new(nvmc);
+ let mut updater = FirmwareUpdater::default();
loop {
button.wait_for_any_edge().await;
if button.is_low() {
- let mut updater = updater::new();
let mut offset = 0;
for chunk in APP_B.chunks(4096) {
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 18bb6330..18bb6330 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 00000000..840faa62
--- /dev/null
+++ b/examples/boot/stm32l0/.cargo/config.toml
@@ -0,0 +1,6 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace your chip as listed in `probe-run --list-chips`
+runner = "probe-run --chip STM32L072CZTx"
+
+[build]
+target = "thumbv6m-none-eabi"
diff --git a/examples/boot/stm32l0/Cargo.toml b/examples/boot/stm32l0/Cargo.toml
new file mode 100644
index 00000000..2e093d77
--- /dev/null
+++ b/examples/boot/stm32l0/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+authors = ["Ulf Lilleengen <lulf@redhat.com>"]
+edition = "2018"
+name = "embassy-boot-stm32l0-examples"
+version = "0.1.0"
+
+[dependencies]
+embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
+embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
+embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-128", "invert-erase", "thumbv6"] }
+embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
+
+defmt = { version = "0.3", optional = true }
+defmt-rtt = { version = "0.3", optional = true }
+panic-reset = { version = "0.1.1" }
+embedded-hal = { version = "0.2.6" }
+
+cortex-m = "0.7.3"
+cortex-m-rt = "0.7.0"
+
+[features]
+defmt = [
+ "dep:defmt",
+ "embassy-stm32/defmt",
+ "embassy-boot-stm32/defmt",
+]
diff --git a/examples/boot/stm32l0/README.md b/examples/boot/stm32l0/README.md
new file mode 100644
index 00000000..9c866082
--- /dev/null
+++ b/examples/boot/stm32l0/README.md
@@ -0,0 +1,29 @@
+# Examples using bootloader
+
+Example for STM32L0 demonstrating the bootloader. The example consists of application binaries, 'a'
+which allows you to press a button to start the DFU process, and 'b' which is the updated
+application.
+
+
+## Prerequisites
+
+* `cargo-binutils`
+* `cargo-flash`
+* `embassy-boot-stm32`
+
+## Usage
+
+```
+# Flash bootloader
+cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l072cz,flash-128,invert-erase,thumbv6 --chip STM32L072CZTx
+# Build 'b'
+cargo build --release --bin b
+# Generate binary for 'b'
+cargo objcopy --release --bin b -- -O binary b.bin
+```
+
+# Flash `a` (which includes b.bin)
+
+```
+cargo flash --release --bin a --chip STM32L072CZTx
+```
diff --git a/examples/boot/stm32l0/build.rs b/examples/boot/stm32l0/build.rs
new file mode 100644
index 00000000..e1da6932
--- /dev/null
+++ b/examples/boot/stm32l0/build.rs
@@ -0,0 +1,37 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ if env::var("CARGO_FEATURE_DEFMT").is_ok() {
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+ }
+}
diff --git a/examples/boot/stm32l0/memory.x b/examples/boot/stm32l0/memory.x
new file mode 100644
index 00000000..fd5bf1a5
--- /dev/null
+++ b/examples/boot/stm32l0/memory.x
@@ -0,0 +1,15 @@
+MEMORY
+{
+ /* NOTE 1 K = 1 KiBi = 1024 bytes */
+ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
+ BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
+ FLASH : ORIGIN = 0x08008000, LENGTH = 32K
+ DFU : ORIGIN = 0x08010000, LENGTH = 36K
+ RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
+}
+
+__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
+__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
+
+__bootloader_dfu_start = ORIGIN(DFU);
+__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 00000000..7b9000c9
--- /dev/null
+++ b/examples/boot/stm32l0/src/bin/a.rs
@@ -0,0 +1,48 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy::time::{Duration, Timer};
+use embassy_boot_stm32::FirmwareUpdater;
+use embassy_stm32::exti::ExtiInput;
+use embassy_stm32::flash::Flash;
+use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
+use embassy_stm32::Peripherals;
+use embassy_traits::adapter::BlockingAsync;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+static APP_B: &[u8] = include_bytes!("../../b.bin");
+
+#[embassy::main]
+async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
+ let flash = Flash::unlock(p.FLASH);
+ let mut flash = BlockingAsync::new(flash);
+
+ let button = Input::new(p.PB2, Pull::Up);
+ let mut button = ExtiInput::new(button, p.EXTI2);
+
+ let mut led = Output::new(p.PB5, Level::Low, Speed::Low);
+
+ led.set_high();
+
+ let mut updater = FirmwareUpdater::default();
+ button.wait_for_falling_edge().await;
+ let mut offset = 0;
+ for chunk in APP_B.chunks(128) {
+ let mut buf: [u8; 128] = [0; 128];
+ buf[..chunk.len()].copy_from_slice(chunk);
+ updater
+ .write_firmware(offset, &buf, &mut flash, 128)
+ .await
+ .unwrap();
+ offset += chunk.len();
+ }
+
+ updater.mark_update(&mut flash).await.unwrap();
+ led.set_low();
+ Timer::after(Duration::from_secs(1)).await;
+ cortex_m::peripheral::SCB::sys_reset();
+}
diff --git a/examples/boot/stm32l0/src/bin/b.rs b/examples/boot/stm32l0/src/bin/b.rs
new file mode 100644
index 00000000..ed774fd7
--- /dev/null
+++ b/examples/boot/stm32l0/src/bin/b.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy::executor::Spawner;
+use embassy::time::{Duration, Timer};
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_stm32::Peripherals;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+#[embassy::main]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ let mut led = Output::new(p.PB6, Level::High, Speed::Low);
+
+ loop {
+ led.set_high();
+ Timer::after(Duration::from_millis(500)).await;
+
+ led.set_low();
+ Timer::after(Duration::from_millis(500)).await;
+ }
+}
diff --git a/examples/boot/stm32l1/.cargo/config.toml b/examples/boot/stm32l1/.cargo/config.toml
new file mode 100644
index 00000000..04985720
--- /dev/null
+++ b/examples/boot/stm32l1/.cargo/config.toml
@@ -0,0 +1,6 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace your chip as listed in `probe-run --list-chips`
+runner = "probe-run --chip STM32L151CBxxA"
+
+[build]
+target = "thumbv7m-none-eabi"
diff --git a/examples/boot/stm32l1/Cargo.toml b/examples/boot/stm32l1/Cargo.toml
new file mode 100644
index 00000000..ec396bef
--- /dev/null
+++ b/examples/boot/stm32l1/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+authors = ["Ulf Lilleengen <lulf@redhat.com>"]
+edition = "2018"
+name = "embassy-boot-stm32l1-examples"
+version = "0.1.0"
+
+[dependencies]
+embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
+embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
+embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-256", "invert-erase"] }
+embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
+
+defmt = { version = "0.3", optional = true }
+defmt-rtt = { version = "0.3", optional = true }
+panic-reset = { version = "0.1.1" }
+embedded-hal = { version = "0.2.6" }
+
+cortex-m = "0.7.3"
+cortex-m-rt = "0.7.0"
+
+[features]
+defmt = [
+ "dep:defmt",
+ "embassy-stm32/defmt",
+ "embassy-boot-stm32/defmt",
+]
diff --git a/examples/boot/stm32l1/README.md b/examples/boot/stm32l1/README.md
new file mode 100644
index 00000000..1a9e85a7
--- /dev/null
+++ b/examples/boot/stm32l1/README.md
@@ -0,0 +1,29 @@
+# Examples using bootloader
+
+Example for STM32L1 demonstrating the bootloader. The example consists of application binaries, 'a'
+which allows you to press a button to start the DFU process, and 'b' which is the updated
+application.
+
+
+## Prerequisites
+
+* `cargo-binutils`
+* `cargo-flash`
+* `embassy-boot-stm32`
+
+## Usage
+
+```
+# Flash bootloader
+cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l151cb-a,flash-256,invert-erase --chip STM32L151CBxxA
+# Build 'b'
+cargo build --release --bin b
+# Generate binary for 'b'
+cargo objcopy --release --bin b -- -O binary b.bin
+```
+
+# Flash `a` (which includes b.bin)
+
+```
+cargo flash --release --bin a --chip STM32L151CBxxA
+```
diff --git a/examples/boot/stm32l1/build.rs b/examples/boot/stm32l1/build.rs
new file mode 100644
index 00000000..e1da6932
--- /dev/null
+++ b/examples/boot/stm32l1/build.rs
@@ -0,0 +1,37 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ if env::var("CARGO_FEATURE_DEFMT").is_ok() {
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+ }
+}
diff --git a/examples/boot/stm32l1/memory.x b/examples/boot/stm32l1/memory.x
new file mode 100644
index 00000000..fd5bf1a5
--- /dev/null
+++ b/examples/boot/stm32l1/memory.x
@@ -0,0 +1,15 @@
+MEMORY
+{
+ /* NOTE 1 K = 1 KiBi = 1024 bytes */
+ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
+ BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
+ FLASH : ORIGIN = 0x08008000, LENGTH = 32K
+ DFU : ORIGIN = 0x08010000, LENGTH = 36K
+ RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
+}
+
+__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
+__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
+
+__bootloader_dfu_start = ORIGIN(DFU);
+__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 00000000..7b9000c9
--- /dev/null
+++ b/examples/boot/stm32l1/src/bin/a.rs
@@ -0,0 +1,48 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy::time::{Duration, Timer};
+use embassy_boot_stm32::FirmwareUpdater;
+use embassy_stm32::exti::ExtiInput;
+use embassy_stm32::flash::Flash;
+use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
+use embassy_stm32::Peripherals;
+use embassy_traits::adapter::BlockingAsync;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+static APP_B: &[u8] = include_bytes!("../../b.bin");
+
+#[embassy::main]
+async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
+ let flash = Flash::unlock(p.FLASH);
+ let mut flash = BlockingAsync::new(flash);
+
+ let button = Input::new(p.PB2, Pull::Up);
+ let mut button = ExtiInput::new(button, p.EXTI2);
+
+ let mut led = Output::new(p.PB5, Level::Low, Speed::Low);
+
+ led.set_high();
+
+ let mut updater = FirmwareUpdater::default();
+ button.wait_for_falling_edge().await;
+ let mut offset = 0;
+ for chunk in APP_B.chunks(128) {
+ let mut buf: [u8; 128] = [0; 128];
+ buf[..chunk.len()].copy_from_slice(chunk);
+ updater
+ .write_firmware(offset, &buf, &mut flash, 128)
+ .await
+ .unwrap();
+ offset += chunk.len();
+ }
+
+ updater.mark_update(&mut flash).await.unwrap();
+ led.set_low();
+ Timer::after(Duration::from_secs(1)).await;
+ cortex_m::peripheral::SCB::sys_reset();
+}
diff --git a/examples/boot/stm32l1/src/bin/b.rs b/examples/boot/stm32l1/src/bin/b.rs
new file mode 100644
index 00000000..ed774fd7
--- /dev/null
+++ b/examples/boot/stm32l1/src/bin/b.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy::executor::Spawner;
+use embassy::time::{Duration, Timer};
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_stm32::Peripherals;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+#[embassy::main]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ let mut led = Output::new(p.PB6, Level::High, Speed::Low);
+
+ loop {
+ led.set_high();
+ Timer::after(Duration::from_millis(500)).await;
+
+ led.set_low();
+ Timer::after(Duration::from_millis(500)).await;
+ }
+}
diff --git a/examples/boot/stm32l4/.cargo/config.toml b/examples/boot/stm32l4/.cargo/config.toml
new file mode 100644
index 00000000..7b6c4c0a
--- /dev/null
+++ b/examples/boot/stm32l4/.cargo/config.toml
@@ -0,0 +1,6 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace your chip as listed in `probe-run --list-chips`
+runner = "probe-run --chip STM32L475VG"
+
+[build]
+target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32l4/Cargo.toml b/examples/boot/stm32l4/Cargo.toml
new file mode 100644
index 00000000..394f26a1
--- /dev/null
+++ b/examples/boot/stm32l4/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+authors = ["Ulf Lilleengen <lulf@redhat.com>"]
+edition = "2018"
+name = "embassy-boot-stm32l4-examples"
+version = "0.1.0"
+
+[dependencies]
+embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
+embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] }
+embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-2k"] }
+embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
+
+defmt = { version = "0.3", optional = true }
+defmt-rtt = { version = "0.3", optional = true }
+panic-reset = { version = "0.1.1" }
+embedded-hal = { version = "0.2.6" }
+
+cortex-m = "0.7.3"
+cortex-m-rt = "0.7.0"
+
+[features]
+defmt = [
+ "dep:defmt",
+ "embassy-stm32/defmt",
+ "embassy-boot-stm32/defmt",
+]
diff --git a/examples/boot/stm32l4/README.md b/examples/boot/stm32l4/README.md
new file mode 100644
index 00000000..09e09d6e
--- /dev/null
+++ b/examples/boot/stm32l4/README.md
@@ -0,0 +1,29 @@
+# Examples using bootloader
+
+Example for STM32L4 demonstrating the bootloader. The example consists of application binaries, 'a'
+which allows you to press a button to start the DFU process, and 'b' which is the updated
+application.
+
+
+## Prerequisites
+
+* `cargo-binutils`
+* `cargo-flash`
+* `embassy-boot-stm32`
+
+## Usage
+
+```
+# Flash bootloader
+cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l475vg,flash-2k --chip STM32L475VG
+# Build 'b'
+cargo build --release --bin b
+# Generate binary for 'b'
+cargo objcopy --release --bin b -- -O binary b.bin
+```
+
+# Flash `a` (which includes b.bin)
+
+```
+cargo flash --release --bin a --chip STM32L475VG
+```
diff --git a/examples/boot/stm32l4/build.rs b/examples/boot/stm32l4/build.rs
new file mode 100644
index 00000000..e1da6932
--- /dev/null
+++ b/examples/boot/stm32l4/build.rs
@@ -0,0 +1,37 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ if env::var("CARGO_FEATURE_DEFMT").is_ok() {
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+ }
+}
diff --git a/examples/boot/stm32l4/memory.x b/examples/boot/stm32l4/memory.x
new file mode 100644
index 00000000..fd5bf1a5
--- /dev/null
+++ b/examples/boot/stm32l4/memory.x
@@ -0,0 +1,15 @@
+MEMORY
+{
+ /* NOTE 1 K = 1 KiBi = 1024 bytes */
+ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
+ BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
+ FLASH : ORIGIN = 0x08008000, LENGTH = 32K
+ DFU : ORIGIN = 0x08010000, LENGTH = 36K
+ RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
+}
+
+__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
+__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
+
+__bootloader_dfu_start = ORIGIN(DFU);
+__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 00000000..a5a9e230
--- /dev/null
+++ b/examples/boot/stm32l4/src/bin/a.rs
@@ -0,0 +1,44 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy_boot_stm32::FirmwareUpdater;
+use embassy_stm32::exti::ExtiInput;
+use embassy_stm32::flash::Flash;
+use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
+use embassy_stm32::Peripherals;
+use embassy_traits::adapter::BlockingAsync;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+static APP_B: &[u8] = include_bytes!("../../b.bin");
+
+#[embassy::main]
+async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
+ let flash = Flash::unlock(p.FLASH);
+ let mut flash = BlockingAsync::new(flash);
+
+ let button = Input::new(p.PC13, Pull::Up);
+ let mut button = ExtiInput::new(button, p.EXTI13);
+
+ let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
+ led.set_high();
+
+ let mut updater = FirmwareUpdater::default();
+ button.wait_for_falling_edge().await;
+ let mut offset = 0;
+ for chunk in APP_B.chunks(2048) {
+ let mut buf: [u8; 2048] = [0; 2048];
+ buf[..chunk.len()].copy_from_slice(chunk);
+ updater
+ .write_firmware(offset, &buf, &mut flash, 2048)
+ .await
+ .unwrap();
+ offset += chunk.len();
+ }
+ updater.mark_update(&mut flash).await.unwrap();
+ led.set_low();
+ cortex_m::peripheral::SCB::sys_reset();
+}
diff --git a/examples/boot/stm32l4/src/bin/b.rs b/examples/boot/stm32l4/src/bin/b.rs
new file mode 100644
index 00000000..81427598
--- /dev/null
+++ b/examples/boot/stm32l4/src/bin/b.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy::executor::Spawner;
+use embassy::time::{Duration, Timer};
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_stm32::Peripherals;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+#[embassy::main]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ let mut led = Output::new(p.PA5, Level::High, Speed::Low);
+
+ loop {
+ led.set_high();
+ Timer::after(Duration::from_millis(500)).await;
+
+ led.set_low();
+ Timer::after(Duration::from_millis(500)).await;
+ }
+}
diff --git a/examples/boot/stm32wl/.cargo/config.toml b/examples/boot/stm32wl/.cargo/config.toml
new file mode 100644
index 00000000..60076e06
--- /dev/null
+++ b/examples/boot/stm32wl/.cargo/config.toml
@@ -0,0 +1,6 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace your chip as listed in `probe-run --list-chips`
+runner = "probe-run --chip STM32WLE5JCIx"
+
+[build]
+target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32wl/Cargo.toml b/examples/boot/stm32wl/Cargo.toml
new file mode 100644
index 00000000..9c69f4a6
--- /dev/null
+++ b/examples/boot/stm32wl/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+authors = ["Ulf Lilleengen <lulf@redhat.com>"]
+edition = "2018"
+name = "embassy-boot-stm32wl-examples"
+version = "0.1.0"
+
+[dependencies]
+embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
+embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] }
+embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["flash-2k"] }
+embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
+
+defmt = { version = "0.3", optional = true }
+defmt-rtt = { version = "0.3", optional = true }
+panic-reset = { version = "0.1.1" }
+embedded-hal = { version = "0.2.6" }
+
+cortex-m = "0.7.3"
+cortex-m-rt = "0.7.0"
+
+[features]
+defmt = [
+ "dep:defmt",
+ "embassy-stm32/defmt",
+ "embassy-boot-stm32/defmt",
+]
diff --git a/examples/boot/stm32wl/README.md b/examples/boot/stm32wl/README.md
new file mode 100644
index 00000000..a26a2385
--- /dev/null
+++ b/examples/boot/stm32wl/README.md
@@ -0,0 +1,29 @@
+# Examples using bootloader
+
+Example for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a'
+which allows you to press a button to start the DFU process, and 'b' which is the updated
+application.
+
+
+## Prerequisites
+
+* `cargo-binutils`
+* `cargo-flash`
+* `embassy-boot-stm32`
+
+## Usage
+
+```
+# Flash bootloader
+cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32wl55jc-cm4,flash-2k --chip STM32WLE5JCIx
+# Build 'b'
+cargo build --release --bin b
+# Generate binary for 'b'
+cargo objcopy --release --bin b -- -O binary b.bin
+```
+
+# Flash `a` (which includes b.bin)
+
+```
+cargo flash --release --bin a --chip STM32WLE5JCIx
+```
diff --git a/examples/boot/stm32wl/build.rs b/examples/boot/stm32wl/build.rs
new file mode 100644
index 00000000..e1da6932
--- /dev/null
+++ b/examples/boot/stm32wl/build.rs
@@ -0,0 +1,37 @@
+//! This build script copies the `memory.x` file from the crate root into
+//! a directory where the linker can always find it at build time.
+//! For many projects this is optional, as the linker always searches the
+//! project root directory -- wherever `Cargo.toml` is. However, if you
+//! are using a workspace or have a more complicated build setup, this
+//! build script becomes required. Additionally, by requesting that
+//! Cargo re-run the build script whenever `memory.x` is changed,
+//! updating `memory.x` ensures a rebuild of the application with the
+//! new memory settings.
+
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() {
+ // Put `memory.x` in our output directory and ensure it's
+ // on the linker search path.
+ let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
+ File::create(out.join("memory.x"))
+ .unwrap()
+ .write_all(include_bytes!("memory.x"))
+ .unwrap();
+ println!("cargo:rustc-link-search={}", out.display());
+
+ // By default, Cargo will re-run a build script whenever
+ // any file in the project changes. By specifying `memory.x`
+ // here, we ensure the build script is only re-run when
+ // `memory.x` is changed.
+ println!("cargo:rerun-if-changed=memory.x");
+
+ println!("cargo:rustc-link-arg-bins=--nmagic");
+ println!("cargo:rustc-link-arg-bins=-Tlink.x");
+ if env::var("CARGO_FEATURE_DEFMT").is_ok() {
+ println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+ }
+}
diff --git a/examples/boot/stm32wl/memory.x b/examples/boot/stm32wl/memory.x
new file mode 100644
index 00000000..78dd69c3
--- /dev/null
+++ b/examples/boot/stm32wl/memory.x
@@ -0,0 +1,15 @@
+MEMORY
+{
+ /* NOTE 1 K = 1 KiBi = 1024 bytes */
+ BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
+ BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
+ FLASH : ORIGIN = 0x08008000, LENGTH = 32K
+ DFU : ORIGIN = 0x08010000, LENGTH = 36K
+ RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
+}
+
+__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
+__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
+
+__bootloader_dfu_start = ORIGIN(DFU);
+__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 00000000..b1f4a4a0
--- /dev/null
+++ b/examples/boot/stm32wl/src/bin/a.rs
@@ -0,0 +1,45 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy_boot_stm32::FirmwareUpdater;
+use embassy_stm32::exti::ExtiInput;
+use embassy_stm32::flash::Flash;
+use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
+use embassy_stm32::Peripherals;
+use embassy_traits::adapter::BlockingAsync;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+static APP_B: &[u8] = include_bytes!("../../b.bin");
+
+#[embassy::main]
+async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
+ let flash = Flash::new(p.FLASH);
+ let mut flash = BlockingAsync::new(flash);
+
+ let button = Input::new(p.PA0, Pull::Up);
+ let mut button = ExtiInput::new(button, p.EXTI0);
+
+ let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
+
+ let mut updater = FirmwareUpdater::default();
+ button.wait_for_falling_edge().await;
+ let mut offset = 0;
+ for chunk in APP_B.chunks(2048) {
+ let mut buf: [u8; 2048] = [0; 2048];
+ buf[..chunk.len()].copy_from_slice(chunk);
+ // defmt::info!("Writing chunk at 0x{:x}", offset);
+ updater
+ .write_firmware(offset, &buf, &mut flash, 2048)
+ .await
+ .unwrap();
+ offset += chunk.len();
+ }
+ updater.mark_update(&mut flash).await.unwrap();
+ // defmt::info!("Marked as updated");
+ led.set_high();
+ cortex_m::peripheral::SCB::sys_reset();
+}
diff --git a/examples/boot/stm32wl/src/bin/b.rs b/examples/boot/stm32wl/src/bin/b.rs
new file mode 100644
index 00000000..ffe15b66
--- /dev/null
+++ b/examples/boot/stm32wl/src/bin/b.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use embassy::executor::Spawner;
+use embassy::time::{Duration, Timer};
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_stm32::Peripherals;
+use panic_reset as _;
+
+#[cfg(feature = "defmt-rtt")]
+use defmt_rtt::*;
+
+#[embassy::main]
+async fn main(_spawner: Spawner, p: Peripherals) {
+ let mut led = Output::new(p.PB15, Level::High, Speed::Low);
+
+ loop {
+ led.set_high();
+ Timer::after(Duration::from_millis(500)).await;
+
+ led.set_low();
+ Timer::after(Duration::from_millis(500)).await;
+ }
+}