#usb-device #dfu #no-std

no-std usbd-dfu-rt

USB DFU 运行时类的实现

4个版本 (2个重大更新)

0.3.1 2023年5月15日
0.3.0 2023年3月8日
0.2.0 2022年11月28日
0.1.0 2021年5月26日

嵌入式开发 中排名 480

Download history 8/week @ 2024-03-12 1/week @ 2024-03-19 15/week @ 2024-04-02 133/week @ 2024-04-16 1/week @ 2024-05-07 3/week @ 2024-05-14 1/week @ 2024-05-21 11/week @ 2024-05-28 32/week @ 2024-06-04 54/week @ 2024-06-11 1/week @ 2024-06-18 4/week @ 2024-06-25

每月下载量 95
kiffieboot 中使用

MIT/Apache

20KB
154

usbd-dfu-rt crates.io docs.rs

这是一个Rust包,用于实现USB DFU运行时类,用于与usb-device包一起使用。

DFU运行时类

DFU代表设备固件升级。DFU定义了两个USB类

  • DFU模式:用于传输新固件并刷新设备
  • 运行时:宣布DFU能力,可用来将设备模式更改为DFU升级

此包根据USB DFU类规范版本1.1a实现DFU运行时类。它仅实现运行时类,这意味着它仅实现DFU_DETACH请求。此请求意味着设备应切换到DFU模式,为固件升级做准备。这通常意味着重启到具有DFU功能的引导加载程序,然后处理升级。

用法

要使用此类,用户必须提供一个回调,该回调将执行切换到DFU模式的转换,这非常特定于设备。特定于应用程序的行为由实现DfuRuntimeOps来定义。

  1. 定义将实现DfuRuntimeOps的类型并保留任何附加状态。
  2. DfuRuntimeOps中设置const设置或使用默认值。
  3. 实现DfuRuntimeOps::detach和可选的DfuRuntimeOps::allow
  4. 定期调用DfuRuntimeClass::tick

DfuRuntimeOps::allow 在接收到 DFU_DETACH 请求时被调用,可以用来拒绝该请求或更改超时值。

根据 DfuRuntimeOps::WILL_DETACH 的值,DfuRuntimeOps::detach 被调用的方式不同。当 WILL_DETACH=false 时,此类会等待检测到主机发出的 USB 重置,然后调用 DfuRuntimeOps::detach

WILL_DETACH=true 时,从 DfuRuntimeOps::allow 返回的 timeout 被用于在调用 DfuRuntimeOps::detach 前等待。这通常是所需的,因为设备应该能够用接受的方式响应断开连接请求。否则,主机可能会看到断开连接错误。因为应用程序经常需要执行一些清理操作(禁用外围设备等),这个超时机制可以简化用户逻辑:在 DfuRuntimeOps::allow 返回应用程序清理和启动所需的时间,当调用 DfuRuntimeOps::detach 时,应用程序可以简单地切换到 DFU 模式。对于更复杂的逻辑,DfuRuntimeOps 可以在 DfuRuntimeOps::allow 期间存储一些状态,并在忽略 DfuRuntimeOps::detach 的情况下以自定义方式执行断开连接操作。

示例

一些微控制器可能带有内置的 DFU 负载程序固件。这可以用来以最少的努力实现通过 USB 的完整固件更新 - 我们只需要实现 DFU 运行时类,而 DFU 模式在嵌入式负载程序中实现。STM32F072 微控制器就是一个例子。

在 STM32F072 上,在跳转到嵌入式负载程序之前,我们应该禁用所有外围设备,将它们设置为复位状态。这在我们的应用程序中可能有些问题,但我们可以只执行 CPU 重置,这将重置外围设备,然后跳转到嵌入式负载程序。为此,我们需要一种方式让固件检测到发生了重置,因为我们想跳转到 DFU 负载程序。这可以通过在内存中存储一个魔法值并在重置后立即检查它来实现。

以下代码可用于实现上述逻辑。当接收到DFU_DETACH请求时,将调用detach,设置魔法值并重置MCU。在执行任何初始化RAM的代码之前(由于#[cortex_m_rt::pre_init]属性),将执行jump_bootloader例程,因此魔法值将仍然存储重置前的值。然后检查是否应该跳转到嵌入式引导加载程序。请确保在主循环中调用DfuRuntimeClass::tick

use core::mem::MaybeUninit;
use cortex_m_rt;
use usbd_dfu_rt::DfuRuntimeOps;

const MAGIC_JUMP_BOOTLOADER: u32 = 0xdeadbeef;
const SYSTEM_MEMORY_BASE: u32 = 0x1fffc800;

#[link_section = ".uninit.MAGIC"]
static mut MAGIC: MaybeUninit<u32> = MaybeUninit::uninit();

#[cortex_m_rt::pre_init]
unsafe fn jump_bootloader() {
    if MAGIC.assume_init() == MAGIC_JUMP_BOOTLOADER {
        // reset the magic value not to jump again
        MAGIC.as_mut_ptr().write(0);
        // jump to bootloader located in System Memory
        cortex_m::asm::bootload(SYSTEM_MEMORY_BASE as *const u32);
    }
}

pub struct DFUBootloader;

impl DfuRuntimeOps for DFUBootloader {
    fn detach(&mut self) {
        unsafe { MAGIC.as_mut_ptr().write(MAGIC_JUMP_BOOTLOADER); }
        cortex_m::peripheral::SCB::sys_reset();
    }
}

// Remember to call .tick() regularly!

依赖项

约150KB