Merge pull request #4478 from leftger/example/stm32wba-usb-dfu

STM32WBA usb-dfu example
This commit is contained in:
Dario Nieuwenhuis 2025-07-29 16:41:15 +00:00 committed by GitHub
commit e145a653cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 559 additions and 0 deletions

2
ci.sh
View File

@ -294,6 +294,7 @@ cargo batch \
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32l4 \
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabi --features skip-include --artifact-dir out/examples/boot/stm32wl \
--- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/boot/stm32wb-dfu \
--- build --release --manifest-path examples/boot/application/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/boot/stm32wba-dfu \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
@ -303,6 +304,7 @@ cargo batch \
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg,verify \
--- build --release --manifest-path examples/boot/bootloader/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-stm32/stm32wba65ri,verify \
--- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h743zi \
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --artifact-dir out/examples/wasm \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --artifact-dir out/tests/stm32f103c8 \

View File

@ -0,0 +1,9 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip STM32WBA65RI"
[build]
target = "thumbv8m.main-none-eabihf"
[env]
DEFMT_LOG = "trace"

View File

@ -0,0 +1,32 @@
[package]
edition = "2021"
name = "embassy-boot-stm32wba-dfu-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
embassy-executor = { version = "0.7.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.4.0", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
embassy-stm32 = { version = "0.2.0", path = "../../../../embassy-stm32", features = ["stm32wba65ri", "time-driver-any", "exti"] }
embassy-boot-stm32 = { version = "0.4.0", path = "../../../../embassy-boot-stm32", features = [] }
embassy-embedded-hal = { version = "0.3.1", path = "../../../../embassy-embedded-hal" }
embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb" }
embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["application", "cortex-m"] }
defmt = { version = "1.0.1", optional = true }
defmt-rtt = { version = "1.0.0", optional = true }
panic-reset = { version = "0.1.1" }
embedded-hal = { version = "0.2.6" }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[features]
defmt = [
"dep:defmt",
"dep:defmt-rtt",
"embassy-stm32/defmt",
"embassy-boot-stm32/defmt",
"embassy-sync/defmt",
]

View File

@ -0,0 +1,9 @@
# Examples using bootloader
Example for STM32WBA demonstrating the USB DFU application.
## Usage
```
cargo flash --release --chip STM32WBA65RI
```

View File

@ -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");
}
}

View File

@ -0,0 +1,15 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 80K
BOOTLOADER_STATE : ORIGIN = 0x08014000, LENGTH = 8K
FLASH : ORIGIN = 0x08016000, LENGTH = 120K
DFU : ORIGIN = 0x0803C000, LENGTH = 160K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 400K
}
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);

View File

@ -0,0 +1,2 @@
untrusted comment: signify secret key
RWRCSwAAAAATdHQF3B4jEIoNZrjADRp2LbjJjNdNNzKwTCe4IB6mDNq96pe53nbNxwbdCc/T4hrz7W+Kx1MwrZ0Yz5xebSK5Z0Kh/3Cdf039U5f+eoTDS2fIGbohyUbrtwKzjyE0qXI=

View File

@ -0,0 +1,114 @@
#![no_std]
#![no_main]
use core::cell::RefCell;
#[cfg(feature = "defmt")]
use defmt_rtt as _;
use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareState, FirmwareUpdaterConfig};
use embassy_executor::Spawner;
use embassy_stm32::flash::{Flash, WRITE_SIZE};
use embassy_stm32::usb::{self, Driver};
use embassy_stm32::{bind_interrupts, peripherals};
use embassy_sync::blocking_mutex::Mutex;
use embassy_time::Duration;
use embassy_usb::{msos, Builder};
use embassy_usb_dfu::consts::DfuAttributes;
use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate};
use panic_reset as _;
bind_interrupts!(struct Irqs {
USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
});
// This is a randomly generated GUID to allow clients on Windows to find your device.
//
// N.B. update to a custom GUID for your own device!
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"];
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = embassy_stm32::Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.pll1 = Some(Pll {
source: PllSource::HSI,
prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz
divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS)
frac: Some(0), // Fractional part (disabled)
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV1;
config.rcc.apb2_pre = APBPrescaler::DIV1;
config.rcc.apb7_pre = APBPrescaler::DIV1;
config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
config.rcc.voltage_scale = VoltageScale::RANGE1;
config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
config.rcc.sys = Sysclk::PLL1_R;
}
let p = embassy_stm32::init(config);
let flash = Flash::new_blocking(p.FLASH);
let flash = Mutex::new(RefCell::new(flash));
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0);
firmware_state.mark_booted().expect("Failed to mark booted");
// Create the driver, from the HAL.
let mut ep_out_buffer = [0u8; 256];
let mut config = embassy_stm32::usb::Config::default();
config.vbus_detection = false;
let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config);
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-DFU Runtime example");
config.serial_number = Some("1235678");
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut state = Control::new(firmware_state, DfuAttributes::CAN_DOWNLOAD, ResetImmediate);
let mut builder = Builder::new(
driver,
config,
&mut config_descriptor,
&mut bos_descriptor,
&mut [],
&mut control_buf,
);
// We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.
// Otherwise users need to do this manually using a tool like Zadig.
//
// It seems these always need to be at added at the device level for this to work and for
// composite devices they also need to be added on the function level (as shown later).
builder.msos_descriptor(msos::windows_version::WIN8_1, 2);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
usb_dfu(&mut builder, &mut state, Duration::from_millis(1000), |func| {
// You likely don't have to add these function level headers if your USB device is not composite
// (i.e. if your device does not expose another interface in addition to DFU)
func.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
});
let mut dev = builder.build();
dev.run().await
}

View File

@ -0,0 +1,8 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-rs run --chip STM32WBA65RI"
[build]
target = "thumbv8m.main-none-eabihf"
[env]
DEFMT_LOG = "trace"

View File

@ -0,0 +1,64 @@
[package]
edition = "2021"
name = "stm32wba6-dfu-bootloader-example"
version = "0.1.0"
description = "Example USB DFUbootloader for the STM32WBA series of chips"
license = "MIT OR Apache-2.0"
[dependencies]
defmt = { version = "1.0.1", optional = true }
defmt-rtt = { version = "1.0.0", optional = true }
embassy-stm32 = { path = "../../../../embassy-stm32", features = [] }
embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
embassy-sync = { version = "0.7.0", path = "../../../../embassy-sync" }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.1"
embedded-storage-async = "0.4.0"
cfg-if = "1.0.0"
embassy-usb-dfu = { version = "0.1.0", path = "../../../../embassy-usb-dfu", features = ["dfu", "cortex-m"] }
embassy-usb = { version = "0.5.0", path = "../../../../embassy-usb", default-features = false }
embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" }
[features]
defmt = [
"dep:defmt",
"dep:defmt-rtt",
"embassy-boot-stm32/defmt",
"embassy-stm32/defmt",
"embassy-usb/defmt",
"embassy-usb-dfu/defmt"
]
verify = ["embassy-usb-dfu/ed25519-salty"]
[profile.dev]
debug = 2
debug-assertions = true
incremental = false
opt-level = 'z'
overflow-checks = true
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 'z'
overflow-checks = false
# do not optimize proc-macro crates = faster builds from scratch
[profile.dev.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false
[profile.release.build-override]
codegen-units = 8
debug = false
debug-assertions = false
opt-level = 0
overflow-checks = false

View File

@ -0,0 +1,63 @@
# Bootloader for STM32
This bootloader implementation uses `embassy-boot` and `embassy-usb-dfu` to manage firmware updates and interact with the flash memory on STM32WB55 devices.
## Prerequisites
- Rust toolchain with `cargo` installed
- `cargo-flash` for flashing the bootloader
- `dfu-util` for firmware updates
- `cargo-binutils` for binary generation
## Usage
### 1. Flash the Bootloader
First, flash the bootloader to your device:
```
cargo flash --features embassy-stm32/stm32wba65ri --release --chip STM32WBA65RI
```
### 2. Build and Flash Application
Generate your application binary and flash it using DFU:
```
cargo objcopy --release -- -O binary fw.bin
dfu-util -d c0de:cafe -w -D fw.bin
```
### 3. Sign Updates Before Flashing (Optional)
Currently, embassy-usb-dfu only supports a limited implementation of the generic support for ed25519-based update verfication in embassy-boot. This implementation assumes that a signature is simply concatenated to the end of an update binary. For more details, please see https://embassy.dev/book/#_verification and/or refer to the documentation for embassy-boot-dfu.
To sign (and then verify) application updates, you will first need to generate a key pair:
```
signify-openbsd -G -n -p secrets/key.pub -s secrets/key.sec
tail -n1 secrets/key.pub | base64 -d -i - | dd ibs=10 skip=1 > secrets/key.pub.short
```
Then you will need to sign all you binaries with the private key:
```
cargo objcopy --release -- -O binary fw.bin
shasum -a 512 -b fw.bin | head -c128 | xxd -p -r > target/fw-hash.txt
signify-openbsd -S -s secrets/key.sec -m target/fw-hash.txt -x target/fw-hash.sig
cp fw.bin fw-signed.bin
tail -n1 target/fw-hash.sig | base64 -d -i - | dd ibs=10 skip=1 >> fw-signed.bin
dfu-util -d c0de:cafe -w -D fw-signed.bin
```
Finally, as shown in this example with the `verify` feature flag enabled, you then need to embed the public key into your bootloader so that it can verify update signatures.
N.B. Please note that the exact steps above are NOT a good example of how to manage your keys securely. In a production environment, you should take great care to ensure that (at least the private key) is protected and not leaked into your version control system.
## Troubleshooting
- Make sure your device is in DFU mode before flashing
- Verify the USB VID:PID matches your device (c0de:cafe)
- Check USB connections if the device is not detected
- Make sure the transfer size option of `dfu-util` matches the bootloader configuration. By default, `dfu-util` will use the transfer size reported by the device, but you can override it with the `-t` option if needed.
- Make sure `control_buf` size is larger than or equal to the `usb_dfu` `BLOCK_SIZE` parameter (in this example, both are set to 4096 bytes).

View File

@ -0,0 +1,27 @@
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");
}
}

View File

@ -0,0 +1,18 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 80K
BOOTLOADER_STATE : ORIGIN = 0x08014000, LENGTH = 8K
ACTIVE : ORIGIN = 0x08016000, LENGTH = 120K
DFU : ORIGIN = 0x0803C000, LENGTH = 160K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 400K
}
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);

View File

@ -0,0 +1 @@
gB<EFBFBD><EFBFBD>p<EFBFBD>M<EFBFBD>S<EFBFBD><EFBFBD>z<EFBFBD><EFBFBD>Kg<EFBFBD><19>!<21>F<EFBFBD><46><02><>!4<>r

View File

@ -0,0 +1,158 @@
#![no_std]
#![no_main]
use core::cell::RefCell;
use cortex_m_rt::{entry, exception};
#[cfg(feature = "defmt")]
use defmt_rtt as _;
use embassy_boot_stm32::*;
use embassy_stm32::flash::{Flash, BANK1_REGION, WRITE_SIZE};
use embassy_stm32::usb::Driver;
use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
use embassy_sync::blocking_mutex::Mutex;
use embassy_usb::{msos, Builder};
use embassy_usb_dfu::consts::DfuAttributes;
use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate};
bind_interrupts!(struct Irqs {
USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
});
// This is a randomly generated GUID to allow clients on Windows to find your device.
//
// N.B. update to a custom GUID for your own device!
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"];
// This is a randomly generated example key.
//
// N.B. Please replace with your own!
#[cfg(feature = "verify")]
static PUBLIC_SIGNING_KEY: &[u8; 32] = include_bytes!("../secrets/key.pub.short");
#[entry]
fn main() -> ! {
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.pll1 = Some(Pll {
source: PllSource::HSI,
prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz
divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS)
frac: Some(0), // Fractional part (disabled)
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV1;
config.rcc.apb2_pre = APBPrescaler::DIV1;
config.rcc.apb7_pre = APBPrescaler::DIV1;
config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
config.rcc.voltage_scale = VoltageScale::RANGE1;
config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
config.rcc.sys = Sysclk::PLL1_R;
}
let p = embassy_stm32::init(config);
// Prevent a hard fault when accessing flash 'too early' after boot.
#[cfg(feature = "defmt")]
for _ in 0..10000000 {
cortex_m::asm::nop();
}
let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
let flash = Mutex::new(RefCell::new(layout.bank1_region));
let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
let active_offset = config.active.offset();
let bl = BootLoader::prepare::<_, _, _, 2048>(config);
// Create the driver, from the HAL.
let mut ep_out_buffer = [0u8; 256];
let mut config = embassy_stm32::usb::Config::default();
config.vbus_detection = false;
if bl.state == State::DfuDetach {
let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config);
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-DFU Bootloader example");
config.serial_number = Some("1235678");
let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
let mut buffer = AlignedBuffer([0; WRITE_SIZE]);
let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut control_buf = [0; 4096];
#[cfg(not(feature = "verify"))]
let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate);
#[cfg(feature = "verify")]
let mut state = Control::new(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate, PUBLIC_SIGNING_KEY);
let mut builder = Builder::new(
driver,
config,
&mut config_descriptor,
&mut bos_descriptor,
&mut [],
&mut control_buf,
);
// We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.
// Otherwise users need to do this manually using a tool like Zadig.
//
// It seems these always need to be at added at the device level for this to work and for
// composite devices they also need to be added on the function level (as shown later).
builder.msos_descriptor(msos::windows_version::WIN8_1, 2);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state, |func| {
// You likely don't have to add these function level headers if your USB device is not composite
// (i.e. if your device does not expose another interface in addition to DFU)
func.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
});
let mut dev = builder.build();
embassy_futures::block_on(dev.run());
}
unsafe { bl.load(BANK1_REGION.base + active_offset) }
}
#[no_mangle]
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
unsafe extern "C" fn HardFault() {
cortex_m::peripheral::SCB::sys_reset();
}
#[exception]
unsafe fn DefaultHandler(_: i16) -> ! {
const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16;
panic!("DefaultHandler #{:?}", irqn);
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
cortex_m::asm::udf();
}