
嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载导读embassy-usb-dfu是 embassy 生态中基于embassy-boot实现的 USB DFU 1.1Device Firmware Upgrade协议库为无操作系统no_std的嵌入式设备提供通过 USB 线升级固件的完整能力。它把USB 协议栈侧如何与主机 dfu-util 交互与启动加载侧如何把收到的固件写入 Flash 并在下次启动时交换解耦前者依赖embassy-usb的 DFU 类后者复用embassy-boot的BlockingFirmwareUpdater。阅读本文后你将掌握该库的两种工作模式DFU 协议模式与 DFU runtime 模式、如何选择与组合 feature、如何配置签名验证ed25519并能基于仓库中的 STM32WB 参考实现写出可实际烧录的 USB DFU 启动加载器。一、整体架构一个库、两种模式、两个 feature从 embassy-usb-dfu/README.md 的定义看该库按用户启用的 feature 分为两个相互独立的组件DFU 协议模式dfufeature对应 USB IF 描述的传输阶段transfer phaseDFU 协议。设备以DFU 目标设备身份出现在总线上接收主机发来的固件数据块。它支持DFU_DNLOAD下载固件是否支持由用户标记并会在一次 DFU 事务完成后自动复位芯片DFU_GETSTATUS、DFU_GETSTATE、DFU_ABORT、DFU_CLRSTATUS等状态管理请求无需用户干预即自动应答。DFU runtime 模式applicationfeature让普通应用固件在自身 USB 描述符中暴露一个 DFU 接口向主机宣告本设备支持 USB DFU 升级并允许主机通过DFU_DETACH将设备复位进入启动加载器。当设备按标准流程观察到 detach/reset 请求时会在启动加载器状态区bootloader state in flash写入新的 DFU magic 数字然后复位系统下次启动时启动加载器根据该 magic 进入 DFU 模式。对应到仓库源码 embassy-usb-dfu/Cargo.toml[features] dfu [] application [] defmt [dep:defmt, embassy-boot/defmt, embassy-usb/defmt] log [dep:log] cortex-m [dep:cortex-m] esp32c3-hal [dep:esp32c3-hal] ed25519-dalek [embassy-boot/ed25519-dalek, _verify] ed25519-salty [embassy-boot/ed25519-salty, _verify] # Internal features _verify []需要特别说明的是两个模式并非互斥CHANGELOG.md见 embassy-usb-dfu/CHANGELOG.md 0.3.0 条目明确记录了 Allow enabling theapplicationanddfufeature at the same time。模块导出逻辑在 embassy-usb-dfu/src/lib.rs#[cfg(feature dfu)] pub mod dfu; #[cfg(all(feature dfu, not(feature application)))] pub use self::dfu::*; #[cfg(feature application)] pub mod application; #[cfg(all(feature application, not(feature dfu)))] pub use self::application::*;只开dfu或只开application时顶层use embassy_usb_dfu::*会直接拿到对应模式的 API同时开启时两个模块embassy_usb_dfu::dfu::*与embassy_usb_dfu::application::*均可用由开发者显式选择。Cargo.toml中[package.metadata.embassy]与[package.metadata.embassy_docs]也表明库的默认构建目标为thumbv7em-none-eabiCortex-M并分别以dfu、application两种 flavor 构建文档。二、DFU 协议模式dfufeature 的实现原理2.1 核心类型与构造流程DFU 协议模式的核心实现位于 embassy-usb-dfu/src/dfu.rs。它内部维护一个FirmwareHandler包装了embassy-boot的阻塞式固件更新器pub struct FirmwareHandlerd, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize { updater: BlockingFirmwareUpdaterd, DFU, STATE, offset: usize, buf: AlignedBufferBLOCK_SIZE, reset: RST, #[cfg(feature _verify)] public_key: static [u8; 32], }DFU实现embedded_storage::nor_flash::NorFlash的 DFU 分区写入固件的地方STATE实现NorFlash的启动加载器状态分区记录 magic、交换标记等RST实现库内Resettrait 的复位器BLOCK_SIZEUSB 单次传输的最大块大小编译期常量同时也是内部对齐缓冲AlignedBufferBLOCK_SIZE的大小启用_verify即用户选了某个 ed25519 feature时额外携带static [u8; 32]的验签公钥。构造一个可用的 DFU 状态机通常走new_state函数pub fn new_stated, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize( updater: BlockingFirmwareUpdaterd, DFU, STATE, attrs: DfuAttributes, reset: RST, #[cfg(feature _verify)] public_key: static [u8; 32], ) - Stated, DFU, STATE, RST, BLOCK_SIZE它把FirmwareHandler塞进embassy_usb::class::dfu::dfu_mode::DfuState该类型通过pub use以UsbDfuState之名从 embassy-usb-dfu/src/dfu.rs 再导出。DfuAttributes决定了设备向主机宣告的能力位。2.2 将 DFU 接口挂到 USB 设备上pub fn usb_dfud, D: Driverd, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize( builder: mut Builderd, D, state: d mut Stated, DFU, STATE, RST, BLOCK_SIZE, func_modifier: impl Fn(mut FunctionBuilder_, d, D), )该函数向传入的embassy_usb::Builder添加 DFU 接口描述符并把state注册为 USB 控制端点的处理器。第三个参数func_modifier允许对生成的接口做额外定制例如为复合设备添加 MS OS 描述符见下文示例。底层直接委托给dfu_mode::usb_dfu(builder, state, BLOCK_SIZE, func_modifier)即embassy-usb的独立 DFU 类。2.3 Handler 回调固件落盘的完整数据流FirmwareHandler实现了dfu_mode::Handlertrait共四个回调对应 DFU 下载生命周期start()主机发起下载首个DFU_DNLOAD请求前时被调用将写入偏移offset清零。write(mut self, data: [u8])每收到一个 USB 块即调用。实现首先检查data.len() BLOCK_SIZE超限直接返回Status::ErrUnknown随后把数据拷入AlignedBuffer保证 Flash 写入对齐调用self.updater.write_firmware(self.offset, buf)落盘成功后累加offset。finish()manifestation 阶段收到最后一个同步块后触发。此时根据是否开启验证走两条路径详见第五节。system_reset()完成下载后调用self.reset.sys_reset()触发系统复位以便启动加载器交换固件。2.4 错误到 DFU Status 的映射write/finish返回的错误会被firmware_error_to_status翻译成 USB DFU 状态码embassy-usb-dfu/src/dfu.rsembassy-boot错误对应 DFU StatusFlash(NorFlashErrorKind::NotAligned)ErrWrite0x03Flash(NorFlashErrorKind::OutOfBounds)ErrAddress0x04Flash(_)其他ErrUnknown0x06Signature(_)验签失败ErrVerify0x07BadStateErrUnknown0x06这些状态码定义在embassy-usb的 embassy-usb/src/class/dfu/consts.rsErrVerify 0x07等主机 dfu-util 会据此向用户报告失败原因。三、DFU runtime 模式applicationfeatureDFU runtime 模式在 embassy-usb-dfu/src/application.rs 中实现内容非常精简——它直接再导出embassy-usb的 DFU 应用模式类pub use embassy_usb::class::dfu::app_mode::{DfuState, Handler, usb_dfu}; pub use embassy_usb::class::dfu::consts::DfuAttributes;也就是说runtime 模式复用embassy-usb的app_mode实现应用固件在枚举时向主机报告一个支持 DFU 的接口含功能描述符中的WILL_DETACH位主机调用DFU_GETSTATUS查询状态、发送DFU_DETACH请求后设备端处理 detach 动作——这正是 README 所述支持DFU_GETSTATUS和DFU_DETACH。而写入 DFU magic 数字并复位这一关键动作发生在embassy-boot侧。查看 embassy-boot/src/lib.rs 的 magic 定义pub(crate) const REVERT_MAGIC: u8 0xC0; pub(crate) const BOOT_MAGIC: u8 0xD0; pub(crate) const SWAP_MAGIC: u8 0xF0; pub(crate) const DFU_DETACH_MAGIC: u8 0xE0;启动加载器在prepare后根据状态区 magic 判定当前状态State::DfuDetach0xE0即应用请求进入 DFU 模式。在FirmwareUpdaterState/BlockingFirmwareState中有对应方法/// Mark to trigger USB DFU device on next boot. pub fn mark_dfu(mut self) - Result(), FirmwareUpdaterError { self.set_magic(DFU_DETACH_MAGIC) }因此 runtime 模式的完整链路是应用收到 detach → 向状态区写DFU_DETACH_MAGIC0xE0→ 系统复位 → 启动加载器读到State::DfuDetach→ 启动 DFU 协议模式并等待主机下载固件。这两段逻辑分别属于应用固件与启动加载器正好由application与dfu两个 feature 对应。四、跨平台复位抽象Resettrait 与ResetImmediate无论哪个模式最终都需要复位系统而不同芯片架构的复位方式不同。库在 embassy-usb-dfu/src/lib.rs 定义了平台无关的抽象pub trait Reset { /// Reset the device. fn sys_reset(self); }并提供了开箱即用的ResetImmediate启用cortex-mfeature 时cortex_m::peripheral::SCB::sys_reset()即标准 Cortex-M 系统复位启用esp32c3-halfeature 时调用esp32c3_hal::reset::software_reset()后进入死循环。如果默认行为不满足需求例如需要先关闭外设、落盘日志再复位可以实现自己的Reset结构体并作为new_state/FirmwareHandler的参数传入Handler::system_reset()会调用你的实现。五、固件签名验证ed25519 与 64 字节签名5.1 embassy-boot 的验证能力与 embassy-usb-dfu 的简化约定README 明确指出embassy-boot本身提供基于 ed25519 的更新二进制签名验证能力协议手册中描述了签名拼接在更新二进制末尾的做法但embassy-boot 并不强制这种分布方式对签名如何随二进制分发保持灵活而embassy-usb-dfu 当前实现假设签名固定 64 字节并拼接在更新二进制末尾因为这是与 usb-dfu 机制配合的最简单方式——也就是说embassy-usb-dfu 目前不具备 embassy-boot 那样的灵活性。该约定直接体现在finish()的源码中embassy-usb-dfu/src/dfu.rs#[cfg(feature _verify)] let update_res: Result(), FirmwareUpdaterError { const SIGNATURE_LEN: usize 64; let mut signature [0; SIGNATURE_LEN]; let update_len (self.offset - SIGNATURE_LEN) as u32; self.updater.read_dfu(update_len, mut signature).and_then(|_| { self.updater .verify_and_mark_updated(self.public_key, signature, update_len) }) }; #[cfg(not(feature _verify))] let update_res self.updater.mark_updated();关键点update_len offset - 64把最后 64 字节当作签名前面的内容视为固件本体签名从 DFU 分区读出后调用embassy-boot的verify_and_mark_updated(public_key, signature, update_len)该函数位于 embassy-boot/src/firmware_updater/blocking.rs内部校验签名并写交换标记验签失败会返回FirmwareUpdaterError::Signature(_)最终映射为Status::ErrVerify上报主机未开启验证时直接mark_updated()写SWAP_MAGIC0xF0见 embassy-boot/src/lib.rs下次启动即交换固件。5.2 如何启用验证在Cargo.toml中为embassy-usb-dfu开启ed25519-dalek或ed25519-salty之一两者都会连带开启内部_verifyfeature 并透传给embassy-bootembassy-usb-dfu { version 0.3.0, path embassy-usb-dfu, features [dfu, cortex-m, ed25519-salty] }README 明确推荐ed25519-salty基于 Salty 的常量时间实现通常比 dalek 更省资源、更适合 MCU。启用后构造new_state时需要额外传入 32 字节验签公钥static PUBLIC_SIGNING_KEY: [u8; 32] include_bytes!(../secrets/key.pub.short); let mut state new_state(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate, PUBLIC_SIGNING_KEY);六、实战集成以 STM32WB 参考启动加载器为例仓库在 examples/boot/bootloader/stm32wb-dfu 提供了一个完整的 USB DFU 启动加载器示例是理解集成方式的最佳范本。6.1 入口逻辑按启动状态分流主程序examples/boot/bootloader/stm32wb-dfu/src/main.rs先准备 Flash 布局并调用BootLoader::prepare然后检查bl.statelet bl BootLoader::prepare::_, _, _, 2048(config); if bl.state State::DfuDetach { // 构建 USB DFU 设备并运行 ... } unsafe { bl.load(BANK1_REGION.base() active_offset) }只有当状态区 magic 为DFU_DETACH_MAGIC时才进入 DFU 模式否则直接加载活动固件。这正是第五节所述 runtime 模式写 magic → 复位 → 进入 DFU闭环的接收端。6.2 DFU 设备初始化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]; let mut state new_state(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate); let mut builder Builder::new( driver, config, mut config_descriptor, mut bos_descriptor, mut [], mut control_buf, );要点FirmwareUpdaterConfig::from_linkerfile_blocking从链接脚本linkerfile自动解析 DFU 分区与状态分区地址无需硬编码WRITE_SIZE取自embassy_stm32::flash保证写入对齐DfuAttributes::CAN_DOWNLOAD声明设备支持下载bit 0定义见 embassy-usb/src/class/dfu/consts.rscontrol_buf4096 字节是 USB 控制端点缓冲示例的 README 特别提醒control_buf必须不小于usb_dfu的BLOCK_SIZE参数本例均为 4096。6.3 注册 DFU 接口并启动设备usb_dfu::_, _, _, _, 4096(mut builder, mut state, |func| { 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());示例还展示了两个工程细节在设备级和接口级都添加了 MS OS 描述符使 Windows 自动加载 WinUSB 驱动省去手动用 Zadig 装驱动的步骤示例注释说明复合设备需要在函数级也添加这些描述符DEVICE_INTERFACE_GUIDS是随机生成的 GUID注释明确要求开发者替换为自己的 GUID若启用verifyfeaturePUBLIC_SIGNING_KEY通过include_bytes!嵌入secrets/key.pub.short。6.4 构建与烧录命令示例的 examples/boot/bootloader/stm32wb-dfu/README.md 给出了完整命令链先烧录启动加载器cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx应用侧生成二进制并用 dfu-util 升级cargo objcopy --release -- -O binary fw.bin dfu-util -d c0de:cafe -w -D fw.bin设备默认 VID:PID 为c0de:cafe与示例代码embassy_usb::Config::new(0xc0de, 0xcafe)对应。排障要点确认设备处于 DFU 模式确认 VID:PID 匹配若设备未被识别检查 USB 连接确保 dfu-util 的传输大小与启动加载器配置一致——dfu-util 默认使用设备报告的功能描述符中的 transfer size必要时用-t覆盖保证control_buf大于等于BLOCK_SIZE。七、带签名验证的升级流程当启动加载器以verifyfeature透传ed25519-salty构建后应用二进制必须附带签名才能通过校验。参考 examples/boot/bootloader/stm32wb-dfu/README.md 的流程先生成密钥对公钥需嵌入启动加载器如secrets/key.pub.shortsignify-openbsd -G -n -p secrets/key.pub -s secrets/key.sec tail -n1 secrets/key.pub | base64 -d -i - | dd ibs10 skip1 secrets/key.pub.short再对每次构建的固件签名并把 64 字节签名拼接到二进制末尾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 ibs10 skip1 fw-signed.bin dfu-util -d c0de:cafe -w -D fw-signed.bin该流程与源码中的约定完全对应签名恰为 64 字节且位于文件末尾finish()中update_len offset - 64即可切分。示例 README 同时强调上述脚本不是安全密钥管理的示范生产环境中应严格保护私钥、避免其进入版本控制系统。八、测试与验证仓库自带的 DFU 行为测试embassy-usb-dfu/tests/usb_dfu_test.rs 提供了不依赖真实硬件的集成测试可用于理解DfuAttributes各组合下的协议行为用InMemoryFlashPartitionRefCell[u8; N]实现的NorFlash模拟 DFU 分区与状态分区NoopReset实现Resettrait复位为空操作便于在宿主机上跑完整下载流程InMemoryDfu实现dfu_core::DfuIo把read_control/write_control直接路由到UsbDfuState的控制端点处理测试用dfu_core::sync::DfuSync::download_from_slice从主机视角驱动一次完整固件下载然后断言 DFU 分区内容与固件逐字节一致。四个测试用例覆盖了DfuAttributes的典型组合bit 定义均来自 embassy-usb/src/class/dfu/consts.rs#[test] fn test_usb_dfu_manifestation_tolerant_will_detach() { usb_dfu(DfuAttributes::CAN_DOWNLOAD | DfuAttributes::MANIFESTATION_TOLERANT | DfuAttributes::WILL_DETACH); } #[test] fn test_usb_dfu_manifestation_tolerant() { ... } #[test] fn test_usb_dfu_will_detach() { ... } #[test] fn test_usb_dfu() { ... }相关属性含义属性位值含义WILL_DETACH0b0000_1000设备会在完成下载后自行 detach无需主机再发 detach 请求MANIFESTATION_TOLERANT0b0000_0100设备在 manifestation 阶段容忍总线复位不等明确应答即完成CAN_DOWNLOAD0b0000_0001设备支持固件下载测试同时验证了功能描述符中各能力位can_download、manifestation_tolerant、will_detach与属性的一致性为移植到新平台后快速回归协议行为提供了可复用的模板。九、小结embassy-usb-dfu把 USB DFU 1.1 协议与embassy-boot的固件管理机制桥接起来形成了清晰的职责分工协议层由embassy-usb的 DFU 类dfu_mode/app_mode负责 USB 描述符、控制请求与状态机传输层FirmwareHandler把 USB 块写入 DFU 分区BlockingFirmwareUpdater错误按 DFU 状态码映射决策层Resettrait、magic 数字DFU_DETACH_MAGIC/SWAP_MAGIC与启动加载器状态机共同决定何时进 DFU、何时交换固件安全层ed25519-salty/ed25519-dalek提供 64 字节签名追加式的固件验签能力。对开发者而言落地一个 USB DFU 升级方案只需三步在启动加载器中用dfufeature 注册 DFU 接口参考 examples/boot/bootloader/stm32wb-dfu在应用固件中用applicationfeature 暴露 DFU 能力并实现 detach 处理最后按需开启签名验证即可用dfu-util -d c0de:cafe -D fw.bin完成从主机到设备的安全固件升级。赞分享嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载相关推荐ESP-IDF USB DFU 固件升级指南idf.py dfu 镜像构建、烧录与跨平台配置详解ESP IDF USB DFU 固件升级指南idf.py dfu 镜像构建、烧录与跨平台配置详解 Device Firmware UpgradeDFU让物联网嵌入式MicroPython Arduino Portenta C33 固件烧录指南基于 dfu-util 的 DFU Bootloader 刷机实战MicroPython Arduino Portenta C33 固件烧录指南基于 dfu util 的 DFU Bootloader 刷机实战 本篇指南围绕嵌入式语言运行时编程语言解释器编译器物联网系统编程ESP-IDF USB 设备固件升级DFU实战指南构建、烧录与疑难排查ESP IDF USB 设备固件升级DFU实战指南构建、烧录与疑难排查 导读 本指南围绕乐鑫 ESP IDF 官方开发框架当前仓库 GitHub_Tre物联网嵌入式上一篇Once一个用于管理一次性操作的Android库下一篇如何使用HollyViewPager打造惊艳的Android滑动界面创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考