当前位置: 首页 > news >正文

Adafruit_nRF52_Bootloader 使用 uf2

烧录Adafruit_nRF52_Bootloader

openocd -f interface/stlink.cfg -f target/nrf52.cfg -c "gdb_flash_program enable" -c "gdb_breakpoint_override hard" -c "init" -c "reset halt" -c "flash write_image erase ./nice_nano_bootloader-0.9.2_s140_6.1.1.hex"

制作 uf2

[env:adafruit_feather_nrf52840]
platform = nordicnrf52
framework = zephyr
board = adafruit_feather_nrf52840extra_scripts = post:utils/generate_bin_uf2.py
# 在 platformio.ini 或 SConscript 中
from SCons.Script import DefaultEnvironmentenv = DefaultEnvironment()# 定义 Post Action
def add_post_action(target, source, env):import osimport subprocessbuild_dir = env.subst("$BUILD_DIR")prog_name = env.subst("$PROGNAME")elf_file = os.path.join(build_dir, prog_name + ".elf")bin_file = os.path.join(build_dir, prog_name + ".bin")uf2_file = os.path.join(build_dir, prog_name + ".uf2")# 调用你的现有 Python 逻辑print(f"Generating BIN from {elf_file}...")subprocess.run([env.subst("$OBJCOPY"),"-O", "binary",elf_file,bin_file], check=True)print(f"Generating UF2 from {bin_file}...")subprocess.run(["python3","utils/uf2conv.py",bin_file,"-c","-b", "0x26000","-f", "0xADA52840","-o", uf2_file], check=True)print(f"---------------- UF2 generated: {uf2_file}")# 添加 Post Action 到程序构建完成事件
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", add_post_action)

检查

 python ./utils/uf2conv.py -i /home/link/Code/nrf52840/zephyr-blink/.pio/build/adafruit_feather_nrf52840/firmware.uf2
--- UF2 File Header Info ---
Family ID is NRF52840, hex value is 0xada52840
Target Address is 0x00026000
All block flag values consistent, 0x2000

根据你贴的输出,UF2 文件本身头信息是:

  • Family ID: 0xADA52840 ✅(和你的 MCU 匹配)
  • Target Address: 0x00026000
  • Block size: 0x2000

从这个信息来看,UF2 文件被正确生成了,但你说刷入后 运行不起来,这通常意味着 程序的实际启动地址和 UF2 文件写入的地址不匹配


核心问题

Target Address = 0x26000 表示 UF2 的固件会被写入 MCU 的 0x26000 地址
但是:

  1. MCU 的 **向量表(Vector Table)**必须在复位后 MCU 能正确找到中断入口。
  2. 如果 .elf 文件的 link address 不匹配 0x26000,MCU 启动时就找不到正确的入口,导致死机或闪烁不了。

换句话说,UF2 写的地址和程序编译链接的地址必须一致


排查方法

  1. 查看 ELF 文件的起始地址:
arm-none-eabi-readelf -l .pio/build/adafruit_feather_nrf52840/firmware.elf | grep "LOAD"
  • 看看哪个地址是 .text 段的起始地址。
  • 如果不是 0x26000,那 UF2 写入的地址就错了。
  1. 确认 PlatformIO 或 Zephyr 的 linker script:
  • Zephyr 默认可能把程序链接在 0x0 或 0x1000,而不是 0x26000。
  • 如果是 Adafruit Feather nRF52840,它的 bootloader 占用 0x0–0x25FFF,所以程序应该从 0x26000 开始。
  • 需要保证 .elf 文件 链接地址和 UF2 target address 一致

解决方案

  1. 调整 linker script,把程序链接到 0x26000(对应 UF2 的 target address)。
  2. 或者 改变 UF2 转换的 -b 参数,使其和 ELF 的 link address 匹配。

💡 建议步骤:

  • readelf 看 ELF 起始地址。
  • 对照 UF2 -b 参数,确保它们完全一致。
  • 再刷入板子测试。

是的,如果你想 UF2 文件刷入后能正常运行,必须保证 ELF 文件的链接地址和 UF2 target address 一致。在你的情况里,你的 UF2 target 是 0x26000,但是 ELF 链接的是 0x0,所以 MCU 启动时找不到正确的向量表。

PlatformIO + Zephyr 中修改链接地址的方法如下:


1️⃣ 使用 board_build.ldscript

PlatformIO 允许通过 board_build.ldscript 指定自定义 linker script:

  1. 找到你当前板子的 linker script。通常在:

    ~/.platformio/packages/framework-zephyr/boards/arm/<board_name>/linker.ld
    

    或者

    .pio/build/<board>/zephyr/linker.ld
    
  2. 拷贝到工程目录,比如:

    boards/feather_nrf52840.ld
    
  3. 修改 flash 起始地址:

    MEMORY
    {FLASH (rx) : ORIGIN = 0x26000, LENGTH = 1024K - 0x26000RAM (rwx)  : ORIGIN = 0x20000000, LENGTH = 256K
    }
    
  4. platformio.ini 中指定:

    [env:adafruit_feather_nrf52840]
    platform = nordicnrf52
    board = adafruit_feather_nrf52840
    framework = zephyr
    board_build.ldscript = boards/feather_nrf52840.ld
    
  5. 重新编译:

    pio run -t build
    

这时生成的 ELF 文件会从 0x26000 起始,和你 UF2 target address 一致。


2️⃣ 注意事项

  • 不要只改 UF2 的 -b 参数,否则会覆盖 bootloader。
  • 保证 flash 起始地址 ≥ bootloader 占用地址(Feather nRF52840 bootloader 一般占到 0x25FFF)。
  • 编译后的 ELF 和 UF2 target address 一致,刷入后就能正常运行。

看了你贴的 platformio SConscript 文件,关键点分析如下:


1️⃣ 为什么你修改 board_build.ldscript 没生效

  1. 你当前使用的是 Zephyr 框架
if "zephyr" in env.get("PIOFRAMEWORK", []):env.SConscript(join(platform.get_package_dir("framework-zephyr"), "scripts", "platformio", "platformio-build-pre.py"),exports={"env": env})
  1. Zephyr 自己的构建系统会生成 linker script(.ld),覆盖 PlatformIO 默认的 board_build.ldscript
  2. 所以即便你在 platformio.ini 里指定了:
board_build.ldscript = boards/feather_nrf52840.ld

也不会生效。ELF 仍然从 0x0 地址链接。


2️⃣ 正确修改 ELF 链接地址的方法(Zephyr + PlatformIO)

方法 A:通过 Kconfig 覆盖 flash 起始地址
  • 在工程根目录创建或修改 prj.conf
CONFIG_FLASH_BASE_ADDRESS=0x26000
  • Zephyr 会根据这个配置在生成的 linker script 里调整 flash 起始地址。
  • 重新编译:
pio run -t clean
pio run
方法 B:修改 Zephyr board YAML 文件
  • 找到板子配置:
~/.platformio/packages/framework-zephyr/boards/arm/adafruit_feather_nrf52840/adafruit_feather_nrf52840.yaml
  • 修改 flash 起始地址:
flash:origin: 0x26000size: 0x100000
  • 重新编译即可。

⚠️ 注意:

  • 起始地址必须大于 bootloader 占用地址(Feather nRF52840 bootloader 占到 0x25FFF)。
  • 编译后的 ELF 文件和 UF2 target address 必须一致。

3️⃣ UF2 转换

  • 保持你的 uf2conv.py 命令:
python utils/uf2conv.py -b 0x26000 -f 0xADA52840 firmware.bin -o firmware.uf2
  • 因为 ELF 现在从 0x26000 链接,UF2 target address 与 ELF 地址一致,刷入后 MCU 可以正常启动。

💡 总结:

  1. PlatformIO 的 board_build.ldscript 对 Zephyr 无效
  2. Zephyr 链接地址必须通过 CONFIG_FLASH_BASE_ADDRESS 或 board YAML 修改。
  3. 修改后重新编译,生成 ELF 就可以和 UF2 target address 对齐。

总结一下你遇到的问题的核心原因:


1️⃣ 链接地址 vs UF2/加载地址

  • 链接地址(Link Address):这是编译器/链接器把程序各段(.text.data.bss 等)在内存中的 逻辑起始位置

    • 编译出来的 ELF 文件里的指令和数据都假设自己从这个地址开始。
    • MCU 启动时,向量表和中断表也基于这个地址。
  • UF2 target/load address:这是 UF2 文件刷入 MCU flash 的 物理地址

    • Bootloader 会把 UF2 写到这个地址。

⚠️ 如果二者不一致

  • MCU 会在启动时跳到向量表地址,但实际程序代码不在那个位置。
  • 即使 UF2 文件写入了 flash,程序也会无法运行或死机。

2️⃣ 为什么直接改 UF2 的 -b 参数无效

  • uf2conv.py -b <address> 只是改变了 UF2 写入 flash 的地址。
  • 但是 ELF 文件的指令和数据仍然假设自己从 原来的链接地址(0x0) 开始。
  • MCU 启动时会跳到 0x26000(UF2 target),但代码内部的绝对地址、向量表、函数指针都仍然指向原来的地址(0x0),程序无法正常运行。

✅ 简单理解:UF2 写错地方,程序本身不“知道”它被搬到哪里


3️⃣ 正确做法

  1. 修改 ELF 链接地址

    • 确保程序在编译/链接阶段就知道它的起始地址是 0x26000

    • 在 Zephyr + PlatformIO 下,可以通过:

      • prj.conf 添加:CONFIG_FLASH_BASE_ADDRESS=0x26000
      • 或修改 board YAML 文件中的 flash 起始地址。
  2. UF2 target address 与 ELF 链接地址一致

    • uf2conv.py -b 0x26000
    • 这样 MCU 启动时跳到向量表,代码就位于正确地址,能正常运行。

💡 核心总结

程序本身的逻辑地址(链接地址)和刷入 flash 的物理地址必须一致,否则 MCU 启动时找不到正确的入口。
直接修改 UF2 写入地址无法修正程序内部的指针和向量表,因此程序无法运行。


好的,我帮你画一个简单的 ASCII 图,展示 ELF 链接地址、BIN 内存布局和 UF2 刷入地址的关系:

 MCU FLASH
+------------------------+
| 0x00000 - 0x25FFF      | <- Bootloader 占用区
+------------------------+
| 0x26000                | <- 程序起始地址 (Link/UF2 Target)
|                        |
|   .text (代码段)       | <- ELF 链接地址也从 0x26000
|   .rodata (只读数据)   |
|                        |
|  .data (初始化数据)    |
|  .bss  (未初始化数据) |
+------------------------+
| 0xFFFFF ...            | <- FLASH 末尾
+------------------------+

解释:

  1. ELF 链接地址

    • 编译时假设 .text.data 等段从 0x26000 开始。
    • 向量表(ISR)也在 0x26000。
  2. BIN 文件

    • objcopy 生成的二进制文件不包含物理地址信息,它只是顺序存放各段数据。
    • 运行时,程序依赖 ELF 链接地址来确定实际内存位置。
  3. UF2 文件

    • UF2 的 -b 参数指定写入 flash 的物理地址。
    • 必须与 ELF 链接地址一致,否则 MCU 跳到向量表时找不到代码。

💡 关键点

  • 如果 ELF 链接地址是 0x0,但 UF2 写入 0x26000 → MCU 启动会找不到向量表 → 程序不跑。
  • 修改 UF2 target 地址不能解决问题,必须让 ELF 链接地址和 UF2 target 一致

Arduino 平台和 Zephyr 在链接器脚本和内存管理上有明显区别:


1. 入口地址和启动方式

  • Arduino (nRF5 / UF2 bootloader)

    • Bootloader 固定在 FLASH 前端(比如 0x0000–0x2000),应用从 bootloader 之后的地址开始。
    • 链接器脚本明确指定应用起始地址 (_estart / .text 段)。
    • 编译生成的 .elf 就是直接放在应用区,UF2 或 MSC bootloader 负责写入 FLASH。
  • Zephyr

    • Zephyr 使用自己的 Bootloader & Vector Table 管理。
    • 可以配置 MCUBOOT 或 MCUboot-compatible bootloader,支持 OTA 升级。
    • 链接器脚本通常由 Zephyr Kconfig 自动生成,入口地址、段布局都是根据 DTS 和配置文件动态生成。

2. 链接器脚本

  • Arduino

    • 手动提供 .ld 文件,用户可以在 Board JSON 或 PlatformIO/IDE 中指定。
    • 通常比较简单,只划分 .text, .data, .bss, .heap, .stack
  • Zephyr

    • 使用 CMake + Kconfig + Zephyr linker scripts 生成完整 .ld,自动管理应用区、bootloader区、闪存分区表、RAM分区等。
    • 修改入口或段布局不直接改 .ld 文件,而是通过 DTS / Kconfig 配置。

3. Bootloader 协议

  • Arduino UF2:直接通过 USB MSC 写入应用区,简单高效,入口地址固定。
  • Zephyr / MCUBOOT:支持多分区、OTA、签名验证,更灵活但复杂。

简单说:

  • Arduino 平台是固定 bootloader + 应用区,链接器脚本简单,可手动改。
  • Zephyr 是完整 RTOS + bootloader/分区管理,入口、内存布局高度自动化,用户改动要通过配置,而不是直接改 .ld

http://www.xdnf.cn/news/1436779.html

相关文章:

  • Spring Cloud Config 核心原理
  • 【C++】编写通用模板代码的重要技巧:T()
  • CICD的持续集成与持续交付和Zabbix
  • 【C++】15. ⼆叉搜索树
  • 室内定位---apriltag 视觉定位demo
  • (四)Python控制结构(条件结构)
  • deepseek7b本地部署技巧,新手也能玩得转
  • 下载 | Win11 官方精简版,系统占用空间极少!(8月更新、Win 11 IoT物联网 LTSC版、适合老电脑安装使用)
  • Flink RuntimeContext和FunctionContext:状态计算的核心桥梁
  • Linux中断实验
  • 数字化转型的终极关怀:以人为本
  • Linux笔记14——shell编程基础-8
  • C#类对象映射AutoMapper
  • QT(2)
  • MTK Linux DRM分析(二十九)- MTK mtk_dsi.c(Part.1)
  • Linux 环境配置 muduo 网络库详细步骤
  • Linux 文本处理三大利器:命令小工具和sed
  • 从理念到实践:三层解耦架构与“无系统”论
  • 基于web的高校学籍管理系统的设计与实现-(源码+LW+可部署)
  • CodeBuddy 在进化:我只输入了一个地址,完成了OneCode3.0基础开发环境的配置构建
  • JWT在线解密/JWT在线解码 - 加菲工具
  • kukekey在线搭建k8sV1.30.4版本
  • 从栈中取出K个硬币的最大面值和-分组背包
  • 【学Python自动化】 8. Python 错误和异常学习笔记
  • 2025年工科生职业发展证书选择与分析
  • 【模型学习】LoRA的原理,及deepseek-vl2下LoRA实现
  • 力扣242:有效的字母异位词
  • JetBrains 2025 全家桶 11合1 Windows直装(含 IDEA PyCharm、WebStorm、DataSpell、DataGrip等
  • C++类和对象(中)- 默认成员函数
  • 什么是数据库管理系统(DBMS)?RDBMS和NoSQL又是什么?