嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载导读本文围绕examples/boot/application/stm32h7这一示例展开讲解如何在 STM32H743 上利用 embassy 生态的 bootloader引导加载程序实现完整的固件升级闭环。读者将掌握如何构建应用 a 更新包 b的双二进制方案、如何用cargo-flash与probe-rs完成 bootloader 与应用烧录、如何通过按键触发 DFU 流程并在复位后自动加载新固件以及示例背后内存布局memory.x与源码实现a.rs/b.rs的底层原理。一、示例概览bootloader 与双应用模型的定位在嵌入式 OTA空中升级场景中bootloader 与应用的协作是核心。本示例位于examples/boot/application/stm32h7演示了 embassy 的 bootloader 体系如何在 STM32H7 上工作bootloader 本身位于examples/boot/bootloader/stm32它使用embassy-boot与 flash 交互负责在复位后决定加载哪个应用应用 asrc/bin/a.rs包含b.bin新固件并通过按键启动 DFU 升级应用 bsrc/bin/b.rs升级后的新应用示例中表现为 LED 闪烁逻辑。为什么需要双二进制因为在没有外部协议栈如串口、USB、网络的简单场景里最直接的传递新固件方式就是把它编译进一个引导应用a中由 a 负责把新固件写入预留的 DFU 分区再重启进入新应用b。示例中涉及的依赖链为embassy-boot-stm32→embassy-stm32flash、exti、gpio、embassy-executor异步运行时、embassy-time定时器、embassy-syncMutex 同步原语完整清单见 examples/boot/application/stm32h7/Cargo.toml。二、前置依赖与工具链在开始构建和烧录前需要安装以下工具对应 README 前置要求工具用途cargo-binutils提供cargo objcopy用于从 ELF 生成裸二进制.bin文件cargo-flash基于probe-rs的烧录工具用于把应用直接烧写到芯片embassy-boot-stm32STM32 平台的 bootloader 支持库本仓库内以路径依赖引入安装方式cargo install cargo-binutils cargo-flashcargo-binutils还会依赖rustup component add llvm-tools提供的 LLVM 工具。由于本示例是#![no_std]#![no_main]的裸机程序构建目标为thumbv7em-none-eabi由Cargo.toml的package.metadata.embassy指定建议使用仓库根目录的rust-toolchain.toml锁定 nightly 工具链。三、完整使用流程构建、打包与烧录按照 README 的 Usage 章节整个流程分三步。1. 烧录 bootloader./flash-boot.sh脚本内容如下见 flash-boot.sh#!/bin/bash probe-rs erase --chip STM32H743ZITx mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x cp memory-bl.x ../../bootloader/stm32/memory.x cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release \ --features embassy-stm32/stm32h743zi \ --chip STM32H743ZITx --target thumbv7em-none-eabihf rm ../../bootloader/stm32/memory.x mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x关键点probe-rs erase先整片擦除确保 flash 状态干净将memory-bl.x临时替换 bootloader 工程的memory.x这样 bootloader 才能编译出适配当前示例分区的链接布局烧录完成后立即还原cargo flash使用stm32h743zifeature 和thumbv7em-none-eabihf目标直接通过 probe 烧录。2. 构建应用 b 并生成二进制cargo build --release --bin b cargo objcopy --release --bin b -- -O binary b.bin--bin b指定只构建src/bin/b.rsobjcopy把 ELF 转换为纯二进制b.bin这一步得到的文件会通过include_bytes!被嵌入应用 a见下文。3. 烧录应用 a内含 b.bincargo flash --release --bin a --chip STM32H743ZITx--bin a构建src/bin/a.rs此时APP_B静态数组已经通过include_bytes!(../../b.bin)把新固件打进二进制因此烧录 a 就等于同时把 b 带到了芯片里。四、内存布局解析两个memory.x的玄机示例目录下存在两个链接脚本理解它们的差异是掌握本示例的关键。应用视角memory.xMEMORY { /* NOTE 1 K 1 KiBi 1024 bytes */ BOOTLOADER : ORIGIN 0x08000000, LENGTH 128K BOOTLOADER_STATE : ORIGIN 0x08020000, LENGTH 128K FLASH : ORIGIN 0x08040000, LENGTH 256K DFU : ORIGIN 0x08100000, LENGTH 512K RAM (rwx) : ORIGIN 0x24000000, LENGTH 368K } __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);bootloader 视角memory-bl.xMEMORY { FLASH : ORIGIN 0x08000000, LENGTH 128K BOOTLOADER_STATE : ORIGIN 0x08020000, LENGTH 128K ACTIVE : ORIGIN 0x08040000, LENGTH 128K DFU : ORIGIN 0x08100000, LENGTH 512K RAM (rwx) : ORIGIN 0x24000000, LENGTH 368K } __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);差异解读两套脚本对0x08000000起点的 128K 命名不同——应用侧叫BOOTLOADER它知道自己被 bootloader 占据bootloader 侧叫FLASH它自己就住在那里起始地址是 STM32H743 主 flash bank1 的基址BOOTLOADER_STATE0x08020000 起128K是共享的状态区用于记录DFU 待更新/更新完成等标志两套脚本导出的__bootloader_state_start/end符号会被FirmwareUpdaterConfig::from_linkerfile_blocking和BootLoaderConfig::from_linkerfile_blocking解析ACTIVE0x08040000 起128K是 bootloader 眼中当前活动应用的存放区与应用侧的FLASH256K对应——应用 a 允许占用更大空间而 bootloader 只会在ACTIVE区域内跳转执行DFU0x08100000 起512K是升级包缓冲区应用 a 把b.bin写到这里RAM 位于0x24000000即 STM32H743 的 AXI-SRAM 区域368K两部分脚本保持一致。这些__bootloader_*符号定义了 flash 各分区的偏移量是 bootloader 与应用之间通过链接脚本握手的协议接口。五、应用 a 的 DFU 升级流程源码拆解src/bin/a.rs 完整实现了等待按键 → 写入新固件 → 标记更新 → 复位的升级流水线。1. 嵌入新固件#[cfg(feature skip-include)] static APP_B: [u8] [0, 1, 2, 3]; #[cfg(not(feature skip-include))] static APP_B: [u8] include_bytes!(../../b.bin);默认情况下通过include_bytes!把步骤二生成的b.bin编译进应用 a启用skip-includefeatureCI 构建中用于跳过真实固件时退化为占位数据见 Cargo.toml 的 feature 定义。2. 初始化 flash 与外设let p embassy_stm32::init(Default::default()); let flash Flash::new_blocking(p.FLASH); let flash Mutex::new(RefCell::new(flash)); let mut button ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); let mut led Output::new(p.PB14, Level::Low, Speed::Low); led.set_high();Flash::new_blocking创建阻塞式 flash 驱动套上MutexRefCell_以便在多任务下共享按键PC13下拉、EXTI 上升沿中断作为升级触发信号Irqs通过bind_interrupts!把EXTI15_10路由到exti::InterruptHandlerLEDPB14用于指示状态。3. 创建升级器并等待按键let config FirmwareUpdaterConfig::from_linkerfile_blocking(flash, flash); let mut magic AlignedBuffer([0; WRITE_SIZE]); let mut updater BlockingFirmwareUpdater::new(config, mut magic.0); let writer updater.prepare_update().unwrap(); button.wait_for_rising_edge().await;FirmwareUpdaterConfig::from_linkerfile_blocking从链接脚本符号自动推导 DFU 区与状态区地址无需手写地址AlignedBuffer([0; WRITE_SIZE])提供 flash 写入所需的对齐缓冲WRITE_SIZE来自embassy_stm32::flashprepare_update()预准备升级环境随后程序挂起在wait_for_rising_edge()上直到用户按下 PC13 按键。4. 分块写入与复位let mut offset 0; let mut buf AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer.write(offset, buf.as_ref()).unwrap(); offset chunk.len() as u32; } updater.mark_updated().unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset();以 4KB 块为单位把b.bin写入 DFU 分区对 STM32H7 的 flash 擦写粒度是合适的对齐块大小mark_updated()在状态区写更新完成标志最后sys_reset()软复位bootloader 复位后检测到新固件将其搬移/跳转到ACTIVE区域执行即进入应用 b。六、应用 b 与 bootloader 侧的实现呼应应用 b新固件本体src/bin/b.rs 是升级目标——一个简单的 LED 闪烁程序Timer::after_millis(300).await; let mut led Output::new(p.PB14, Level::High, Speed::Low); led.set_high(); loop { led.set_high(); Timer::after_millis(500).await; led.set_low(); Timer::after_millis(500).await; }它与应用 a 的LED 常亮等待升级形成可观测差异烧录 a 后 LED 常亮未按按键按下按键完成升级并复位后LED 转为 500ms 周期闪烁即可直观确认 b 已接管运行。bootloader复位后的决策者examples/boot/bootloader/stm32/src/main.rs 展示了 bootloader 侧如何使用同一套内存布局约定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); unsafe { bl.load(BANK1_REGION.base() active_offset) }使用 bank1 区域 flashBootLoader::prepare检查状态区与固件完整性决定直接跳转当前ACTIVE应用或先执行升级与 memory-bl.x 中__bootloader_active_*符号衔接这正是flash-boot.sh在烧录 bootloader 前切换链接脚本的原因。七、内存分区速查与故障排查分区起始地址大小用途两脚本命名Bootloader0x08000000128Kbootloader 代码BOOTLOADER/FLASHBootloader State0x08020000128K升级状态标志BOOTLOADER_STATEActive / App0x08040000128K / 256K当前运行应用ACTIVE/FLASH应用侧 256KDFU0x08100000512K升级包暂存区DFURAM0x24000000368K运行内存AXI-SRAMRAM常见问题排查思路b.bin未生成cargo objcopy前必须先cargo build --release --bin b且工作目录应位于examples/boot/application/stm32h7include_bytes!路径错误APP_B引用的是相对src/bin/的../../b.bin因此b.bin必须放在examples/boot/application/stm32h7/根目录烧录 bootloader 后应用不启动确认flash-boot.sh执行完毕后 bootloader 工程的memory.x已被还原为原始文件升级后仍运行旧应用检查按键是否确实产生上升沿PC13 为下拉配置需外部上拉/按键接高电平触发芯片不匹配所有cargo flash命令均需显式指定--chip STM32H743ZITx。八、延伸将此模式推广到真实 OTA本示例的价值在于展示了一条链接脚本驱动地址布局 → 阻塞式 FirmwareUpdater 写入 DFU 区 → 软复位切换应用的可复现路径。真实产品中通常将b.bin的来源从include_bytes!替换为串口、USB DFU 或网络下载但FirmwareUpdater/BootLoader的核心接口prepare_update、write、mark_updated保持不变。若需要加密签名校验可参考仓库中stm32wb-dfu、stm32wba-dfu等带密钥目录的变体示例若芯片支持双 bank 架构还可参考 examples/boot/bootloader/stm32-dual-bank 的实现思路。参考文件索引示例说明examples/boot/application/stm32h7/README.md应用 aDFU 引导examples/boot/application/stm32h7/src/bin/a.rs应用 b新固件examples/boot/application/stm32h7/src/bin/b.rs烧录脚本examples/boot/application/stm32h7/flash-boot.sh应用链接脚本examples/boot/application/stm32h7/memory.xBootloader 链接脚本examples/boot/application/stm32h7/memory-bl.xBootloader 实现examples/boot/bootloader/stm32/src/main.rs依赖配置examples/boot/application/stm32h7/Cargo.toml赞分享嵌入式物联网异步编程【免费下载链接】embassyModern embedded framework, using Rust and async.项目地址https://gitcode.com/gh_mirrors/em/embassy点击查看免费下载相关推荐STM32WBA USB DFU 引导加载程序实战基于 embassy-boot 与 embassy-usb-dfu 的固件烧录与 ed25519 签名验证STM32WBA USB DFU 引导加载程序实战基于 embassy boot 与 embassy usb dfu 的固件烧录与 ed25519 签名验证嵌入式物联网异步编程Embassy Bootloader 实战STM32L0 双应用 DFU 升级示例examples/boot/application/stm32l0Embassy Bootloader 实战STM32L0 双应用 DFU 升级示例examples/boot/application/stm32l0 本指嵌入式物联网异步编程embassy-usb-dfu 实践指南基于 embassy-boot 的 USB DFU 1.1 固件升级协议实现embassy usb dfu 实践指南基于 embassy boot 的 USB DFU 1.1 固件升级协议实现 导读 embassy usb dfu 是嵌入式物联网异步编程上一篇twitter-sentiment-analysis数据集处理全攻略从原始数据到模型输入下一篇Django Widget Tweaks 使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考