# pyocd 调用 FLM 格式的外部下载算法
记录如何通过 pyocd
调用 Keil 使用的 .FLM
格式外部下载算法,将程序烧录到 STM32H7 系列微控制器的 QSPI Flash 中。
# 项目背景
项目 | 内容 |
---|---|
开发板 | 反客STM32H750XHB6 |
参考工程 | Peakors/STM32H750XBH6_Template |
下载算法 | FK750M6_XBH6_V0.FLM (由开发板资料提供) |
测试环境 | Fedora 42 (经验可跨平台通用) |
# 前置依赖
确保已安装以下工具链:
pyocd
cmake
ninja
或make
arm-none-eabi-gcc
# 编译流程
# 生成 Ninja/Make 构建文件
cmake -S . -B build -G Ninja
# 执行编译
cmake --build build/
# 原理说明
通过在项目根目录创建 pyocd_user.py
脚本,可以自定义 pyOCD 的连接行为。pyOCD 在启动时会自动检测并加载该文件。
我们在脚本中定义一个 will_connect
函数,该函数会在 pyOCD 连接目标板之前被调用。通过这个函数,我们可以向 pyOCD 的内存地图中添加外部 Flash 的信息(如 QSPI Flash),并指定其使用的 .FLM
下载算法。
这种机制使得 pyOCD 能够在不修改内置目标支持的情况下,扩展对外部存储器的烧录能力。
1. 目录结构
- 将下载算法文件
FK750M6_XBH6_V0.FLM
存放到项目下的.config
文件夹中。 - 在项目根目录创建
pyocd_user.py
文件。
2. pyocd_user.py
脚本
# pyocd_user.py
from pyocd.core.memory_map import FlashRegion
import logging
# --- 定义外部 QSPI Flash 的参数 ---
FLM_FILE = ".config/FK750M6_XBH6_V0.FLM" # 存放下载算法的相对路径
QSPI_FLASH_START = 0x90000000
QSPI_FLASH_SIZE = 32 * 1024 * 1024
QSPI_FLASH_BLOCKSIZE = 0x1000
def will_connect(board):
"""
这是一个 pyOCD 代理函数,会在连接前被自动调用。
"""
target = board.target
# 定义外部 Flash 区域
external_flash = FlashRegion(
name="qspi_flash",
start=QSPI_FLASH_START,
length=QSPI_FLASH_SIZE,
blocksize=QSPI_FLASH_BLOCKSIZE,
is_boot_memory=False,
flm=FLM_FILE
)
# 将其添加到内存地图
target.memory_map.add_region(external_flash)
logging.info(f"用户脚本:成功添加外部 QSPI Flash 区域 '{external_flash.name}'")
具体定义参考/home/hao/.local/lib/python3.13/site-packages/pyocd/core/memory_map.py
(pyocd安装路径)
# 烧录指令
使用以下指令将编译生成的 .bin
文件烧录到 QSPI Flash 的起始地址 0x90000000
。
--target stm32h750xx
:指定目标芯片型号。-O connect_mode=under-reset
:设置连接模式为复位下连接。./build/STM32H750XBH6_Template.bin@0x90000000
:指定固件路径和烧录地址。
pyocd flash --target stm32h750xx -O connect_mode=under-reset ./build/STM32H750XBH6_Template.bin@0x90000000
# 日志参考
1. 简洁版日志
❯ pyocd flash --target stm32h750xx -O connect_mode=under-reset ./build/STM32H750XBH6_Template.bin@0x90000000
0000447 I Loading /home/hao/projects/stm32_projects/STM32H750XBH6/CMake/STM32H750XBH6_Template/build/STM32H750XBH6_Template.bin at 0x90000000 [load_cmd]
[==================================================] 100%
0002722 I Erased 65536 bytes (1 sector), programmed 65536 bytes (8 pages), skipped 0 bytes (0 pages) at 28.14 kB/s [loader]
2. 详细版日志 (-v
参数)
❯ pyocd flash --target stm32h750xx -v -O connect_mode=under-reset ./build/STM32H750XBH6_Template.bin@0x90000000
0000346 I Target type is stm32h750xx [board]
0000349 I 用户脚本:成功添加外部 QSPI Flash 区域 'qspi_flash' [pyocd_user]
0000349 I Asserting reset prior to connect [coresight_target]
0000352 I DP IDR = 0x6ba02477 (v2 rev6) [dap]
0000366 I CR: 0x0070003f [target_STM32H750xx]
0000370 I AHB-AP#0 IDR = 0x84770001 (AHB-AP var0 rev8) [discovery]
0000373 I AHB-AP#1 IDR = 0x84770001 (AHB-AP var0 rev8) [discovery]
0000376 I APB-AP#2 IDR = 0x54770002 (APB-AP var0 rev5) [discovery]
0000379 I AHB-AP#0 Class 0x1 ROM table #0 @ 0xe00fe000 (designer=020:ST part=450) [rom_table]
0000381 I [0]<e00ff000:ROM class=1 designer=43b:Arm part=4c7> [rom_table]
0000381 I AHB-AP#0 Class 0x1 ROM table #1 @ 0xe00ff000 (designer=43b:Arm part=4c7) [rom_table]
0000383 I [0]<e000e000:SCS v7-M class=14 designer=43b:Arm part=00c> [rom_table]
0000385 I [1]<e0001000:DWT v7-M class=14 designer=43b:Arm part=002> [rom_table]
0000386 I [2]<e0002000:FPB v7-M class=14 designer=43b:Arm part=00e> [rom_table]
0000387 I [3]<e0000000:ITM v7-M class=14 designer=43b:Arm part=001> [rom_table]
0000389 I [1]<e0041000:ETM M7 class=9 designer=43b:Arm part=975 devtype=13 archid=4a13 devid=0:0:0> [rom_table]
0000391 I [2]<e0043000:CTI CS-400 class=9 designer=43b:Arm part=906 devtype=14 archid=0000 devid=40800:0:0> [rom_table]
0000393 I APB-AP#2 Class 0x1 ROM table #0 @ 0xe00e0000 (designer=020:ST part=450) [rom_table]
0000396 I [2]<e00e3000:SWO CS-400 class=9 designer=43b:Arm part=914 devtype=11 archid=0000 devid=ea0:0:0> [rom_table]
0000398 I [3]<e00e4000:Trace Funnel CS-400 class=9 designer=43b:Arm part=908 devtype=12 archid=0000 devid=32:0:0> [rom_table]
0000399 I [4]<e00e5000:TSGEN class=15 designer=43b:Arm part=101> [rom_table]
0000400 I [5]<e00f0000:ROM class=1 designer=020:ST part=001> [rom_table]
0000401 I APB-AP#2 Class 0x1 ROM table #1 @ 0xe00f0000 (designer=020:ST part=001) [rom_table]
0000404 I [0]<e00f1000:CTI CS-400 class=9 designer=43b:Arm part=906 devtype=14 archid=0000 devid=40800:0:0> [rom_table]
0000406 I [2]<e00f3000:Trace Funnel CS-400 class=9 designer=43b:Arm part=908 devtype=12 archid=0000 devid=34:0:0> [rom_table]
0000408 I [3]<e00f4000:ETF class=9 designer=43b:Arm part=961 devtype=32 archid=0000 devid=380:0:0> [rom_table]
0000410 I [4]<e00f5000:TPIU CS-400 class=9 designer=43b:Arm part=912 devtype=11 archid=0000 devid=a0:0:0> [rom_table]
0000415 I CPU core #0: Cortex-M7 r1p1, v7.0-M architecture [cortex_m]
0000415 I Extensions: [DSP, FPU, FPU_DP, FPU_V5, MPU] [cortex_m]
0000415 I FPU present: FPv5-D16-M [cortex_m]
0000416 I 4 hardware watchpoints [dwt]
0000419 I 8 hardware breakpoints, 1 literal comparators [fpb]
0000431 I Deasserting reset post connect [coresight_target]
0000433 I Creating flash algo for region qspi_flash from: /home/hao/projects/stm32_projects/STM32H750XBH6/CMake/STM32H750XBH6_Template/.config/FK750M6_XBH6_V0.FLM [flm_region_builder]
0000448 I Loading /home/hao/projects/stm32_projects/STM32H750XBH6/CMake/STM32H750XBH6_Template/build/STM32H750XBH6_Template.bin at 0x90000000 [load_cmd]
[==================================================] 100%
0002723 I Erased 65536 bytes (1 sector), programmed 65536 bytes (8 pages), skipped 0 bytes (0 pages) at 28.14 kB/s [loader]
# 实现效果
-
Bootloader 日志 (运行于片内 Flash)
- 程序启动后,首先运行片内 Flash 中的 Bootloader,通过串口打印启动日志。
- Bootloader 日志截图 (乱码系中文编码问题)
-
主程序日志 (运行于外部 QSPI Flash)
- Bootloader 跳转到 QSPI Flash 中执行主程序,主程序通过 USB 虚拟串口打印运行日志。
- USB 虚拟串口日志截图
串口波特率115200
# 特别鸣谢
反客科技:提供开发板资料和.FLM格式下载算法
反客交流群的Peakors大佬:提供STM32H750XHB6的项目模板和openocd外部下载算法,他还制作了很多反客开发板的项目模板和下载算法,GitHub主页。
谷歌Gemini:消化pyocd的文档,提供指导建议和文章排版