Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,23 @@ This will power off the Crazyflie (same as pressing the power button):
```bash
cfcli platform power-off
```

## Restart the STM32 into USB DFU

This restarts the STM32 into its ROM bootloader, the same as holding the power button from off,
so it can be flashed over USB with a DFU tool such as `dfu-util`:

```bash
cfcli platform dfu
```

The command is handled by the nRF51, so the Crazyflie has to be selected with a radio URI, and its
nRF51 firmware has to support it. With the Crazyflie connected over USB, `cfcli` waits for the STM32
to show up in DFU mode (`0483:df11`):

```text
STM32 is in USB DFU mode (0483:df11)
When done, restart it into its firmware with: cfcli platform reboot
```

Once flashing is done, `cfcli platform reboot` restarts the STM32 into its firmware.
5 changes: 5 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,11 @@ enum PlatformCommands {
Sleep,
/// Wake up the platform
Wakeup,
/// Restart the STM32 into its USB DFU bootloader (radio URI only)
///
/// Needs an nRF51 firmware that supports it. Flash with dfu-util, then
/// return to the firmware with `cfcli platform reboot`.
Dfu,
}

#[derive(Debug, Subcommand)]
Expand Down
11 changes: 11 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,17 @@ async fn run() -> Result<()> {
PlatformCommands::Wakeup => {
crazyflie_lib::Crazyflie::power_on_stm32_domain(&link_context, uri.as_str()).await?;
}
PlatformCommands::Dfu => {
if !modules::bootloader::stm32_dfu(&link_context, uri.as_str()).await? {
bail!(
"the STM32 did not show up on USB in DFU mode. The Crazyflie has to be connected \
over USB, and its nRF51 firmware has to support the command. If the STM32 did \
restart, `cfcli platform reboot` brings it back to its firmware."
);
}
println!("STM32 is in USB DFU mode (0483:df11)");
println!("When done, restart it into its firmware with: cfcli platform reboot");
}
}

}
Expand Down
42 changes: 42 additions & 0 deletions src/modules/bootloader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ use cfloader::Bllink;
const TARGET_STM32: u8 = 0xFF;
const TARGET_NRF51: u8 = 0xFE;

// The STM32 ROM bootloader on USB, and how long it gets to show up there
// after the nRF51 has restarted the STM32 into it
const STM32_DFU_VID: u16 = 0x0483;
const STM32_DFU_PID: u16 = 0xDF11;
const STM32_DFU_ENUMERATION_TIMEOUT: Duration = Duration::from_secs(5);

#[derive(Debug)]
struct BootloaderInfo {
id: u8,
Expand All @@ -54,6 +60,7 @@ struct BootloaderInfo {
enum BootloaderCommand {
ResetInit = 0xFF,
Reset = 0xF0,
Stm32Dfu = 0x07,
}

pub fn get_hardcoded_list_of_targets() -> Vec<&'static str> {
Expand Down Expand Up @@ -285,6 +292,41 @@ pub async fn bootloader_versions(link_context: &LinkContext, uri: &str) -> Resul
Ok((stm32.protocol_version, nrf51.protocol_version))
}

/// Restart the STM32 into its ROM bootloader, so it can be flashed over USB DFU
///
/// The nRF51 power-cycles the STM32 with BOOT0 high, as a long press on the
/// power button from off does. The command is handled by the nRF51 itself,
/// which only receives it over the radio: the STM32 firmware does not pass it
/// on from USB. Returns whether the STM32 then showed up on USB in DFU mode.
pub async fn stm32_dfu(link_context: &LinkContext, uri: &str) -> Result<bool> {
if uri.starts_with("usb://") {
bail!("Restarting the STM32 into DFU needs a radio URI, the nRF51 does not get this command over USB");
}

let link = link_context.open_link(uri).await?;
send_command(&link, BootloaderCommand::Stm32Dfu, None).await?;

let deadline = tokio::time::Instant::now() + STM32_DFU_ENUMERATION_TIMEOUT;
while tokio::time::Instant::now() < deadline {
sleep(Duration::from_millis(200)).await;
if stm32_dfu_present()? {
return Ok(true);
}
}

Ok(false)
}

fn stm32_dfu_present() -> Result<bool> {
for device in rusb::devices()?.iter() {
let desc = device.device_descriptor()?;
if desc.vendor_id() == STM32_DFU_VID && desc.product_id() == STM32_DFU_PID {
return Ok(true);
}
}
Ok(false)
}

pub async fn reboot(link_context: &LinkContext, uri: &str,) -> Result<()> {

let link = link_context.open_link(uri).await?;
Expand Down
Loading