调试STM32N647Z0的时候,在Keil MDK里给外部Flash MX25UM51245配置下载算法,一加载工程就弹出来一个“failed to create the download file for mx25um51245 external flash”,这个错误我第一次遇到的时候也懵了一下。字面意思是“为外部Flash创建下载文件失败”,但真正让人头疼的是这个提示没有给出具体的失败原因,Cortex-M55内核的板子、OSPI接口的Flash、加上MDK的下载算法机制,三者的配置任何一个地方不对,都会冒出来这句提示。
这篇文章就把这个问题的排查思路、根因分析、以及从零配置一套可用的外部Flash下载方案的完整过程写清楚。适合正在用STM32N6系列、N647、N657这类带Neural-ART单元的新片子的工程师,也适合所有在Keil里给外部NOR Flash配置编程算法时遇到类似下载文件创建失败的人。
1. 先搞清楚报错到底在说啥
1.1 “下载文件”指的是什么?
Keil MDK里所说的下载文件(download file),并不是我们要烧录的固件,而是针对外部Flash存储器的编程算法文件,也就是常说的FLM文件。当你给目标板配置Flash Download时,Keil需要为每一片Flash指定一个对应的FLM文件,烧录器(ST-Link、J-Link、DAP-Link)通过调试接口把这个FLM算法加载到RAM里运行,用它来完成对外部Flash的擦除、编程和校验。
问题就出在这条链路上:STM32N647Z0本身的内部Flash是没问题的,但外部Flash MX25UM51245需要额外的FLM文件。如果这个文件在工程里找不到、路径不对、文件本身损坏,或者生成这个FLM的底层代码和当前芯片/Flash型号不匹配,Keil在准备阶段就会报“failed to create the download file”。
1.2 这个报错最容易在哪个环节触发
从实际踩坑的经验来看,这个错误不只是在“烧录固件”时才出现。常见触发场景有三个:
- 打开工程后第一次点击Download按钮
- 在Options for Target -> Utilities -> Settings -> Flash Download里点击Add按钮添加外部Flash算法
- 通过STM32CubeProgrammer连接开发板,尝试加载External Loader(.stldr文件)时
第三种情况容易被忽略,ST-Link自带的STM32CubeProgrammer在识别到外部Flash时,如果外部Loader文件没有正确放入安装目录的ExternalLoader文件夹里,也会报出类似的“failed to create the download file”错误。
2. 排查第一步:下载算法文件没到位
2.1 确认FLM文件是否存在
这个错误最朴素的原因就是FLM文件缺失。MX25UM51245不是一颗普通的QuadSPI Flash,它是Macronix的OctalSPI NOR Flash,容量512Mb(64MB),支持1.8V低电压供电,支持DTR双沿传输。ST官方的STM32CubeProgrammer自带的外部Loader列表里,不一定默认包含这颗Flash的加载算法。尤其你用的是N647这种较新的芯片,如果Pack包版本不够新,或者中途换过Flash型号,就很容易出现算法文件没被自动带上的情况。
我的建议是先用一个最直接的方式确认:在Keil里打开工程,点魔术棒图标进入Options for Target,切到Utilities选项卡,点击Settings进入Flash Download页面,看Programming Algorithm列表里有没有MX25UM51245相关的条目。如果列表是空的,或者只有STM32 N647 internal flash,那就说明外部Flash的FLM文件根本没有被加载进来。
2.2 FLM文件路径错误才是重灾区
如果列表里有这条算法但依然报错,甚至点Add时找不到对应的FLM文件,那大概率是路径问题。Keil在加载FLM文件时,优先从以下两个位置寻找:
- 工程目录下的
*.FLM文件(如果你手动拷贝过) - Keil安装目录下的
ARM\Flash文件夹(Pack安装时自动复制过来的)
很多人的工程是从同事那里拷来的,FLM文件虽然在工程压缩包里,但解压后没有放到正确目录,Keil自然找不到。另外,有些同事喜欢把FLM文件直接放在工程目录里,但工程文件通过相对路径引用时指向了上级目录,一旦整个工程移动了位置,路径就失效了。
解决方法是打开Options for Target -> Debug -> Settings,或者直接查看工程文件(.uvprojx)里的<FlashDriverDll>和<ProgrammingAlgorithm>节点,检查FLM文件的路径是否有效。如果工程文件里写着..\ExternalFlash\MX25UM51245.FLM,那就确认这个相对路径从当前.uvprojx文件所在目录出发能不能找到对应文件。
2.3 STM32CubeProgrammer的外部Loader别漏了
在STM32CubeProgrammer这一侧,对应FLM文件的是External Loader(.stldr后缀)。很多人只在Keil里配了FLM,但用CubeProgrammer通过ST-Link直接连接时仍然报同样的错,这是因为CubeProgrammer只认自己安装目录下的Loader文件。
把编译好的外部Loader文件(.stldr)拷贝到C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader目录下,然后重新启动CubeProgrammer。注意,不是随便一个FLM文件都能改成.stldr后缀直接用的,必须是使用STM32CubeMX生成的External Loader工程编译出来的才行。
3. 再深挖一层:MX25UM51245这颗Flash的特殊性
3.1 它不是普通SPI Flash
MX25UM51245G是一颗很典型的“看起来很简单、用起来处处是坑”的Flash。它支持标准的SPI模式,也支持Dual、Quad,但真正的完全体是OctalSPI模式,也就是8根数据线同时传输。工作在Octal DTR模式下时,时钟的上升沿和下降沿都会采样数据,理论吞吐量可以做得非常高,匹配STM32N6系列这种需要大容量外部代码存储、频繁读取AI模型参数的应用场景。
这颗Flash的供电电压是1.8V,不是常见的3.3V。如果你的板子原理图设计时没有做电平转换,或者STM32N647的OSPI引脚供电域配置成了3.3V,Flash通信就会不稳定。这种硬件层面的问题不会直接报“failed to create the download file”,但会导致FLM算法加载后无法正确读取Flash ID,下载流程中断,最后Keil兜底报出来的还是“failed to create the download file”或者“cannot load flash programming algorithm”。
3.2 芯片封装和OSPI引脚的映射关系
STM32N647Z0这个型号的后缀Z0表示LQFP144封装,不同封装的OSPI引脚数量和外设映射不一样。如果PCB设计时引出的OSPI引脚和代码里配置的引脚不一致,FLM算法初始化Flash时就会失败。
调试这个问题的思路是这样的:先用逻辑分析仪抓一下OSPI的CLK和IO0,看看芯片启动后有没有针对外部Flash的时钟信号。如果没有时钟输出,说明FLM算法没有正确初始化OSPI外设;如果有时钟但没有数据响应,多半是Flash的复位引脚或者片选引脚没有拉对。
3.3 时钟配置对下载算法的影响
还有一个非常隐蔽的坑:MX25UM51245支持的最高时钟频率和你要用的OSPI时钟频率必须匹配。STM32N647的内部PLL如果配置得比较高,OSPI的时钟分频没设置好,导致实际输出的时钟频率超过了Flash允许的最大值,FLM算法在读取JEDEC ID时会读到错误的数据,下载自然会失败。
我遇到过一种情况,芯片主频跑800MHz,OSPI时钟直接不分频给到了100MHz以上,MX25UM51245在1.8V电压下能承受的频率上限其实是有限的。把OSPI时钟分频降低之后,下载文件就能正常创建了。所以当FLM文件明明存在、路径也正确但依然报错时,优先怀疑时钟频率配置。
4. 实操:从零配置一套可用的外部Flash下载方案
4.1 准备材料和环境检查清单
在动手之前,先确认手里的工具链是完整的。我建议的清单如下:
| 项目 | 要求 | 备注 |
|---|---|---|
| 开发板 | STM32N647Z0核心板或自制板 | 确认板载MX25UM51245接线正确 |
| 调试器 | ST-Link V3或J-Link V9以上 | Keil和CubeProgrammer共用 |
| MDK版本 | 5.38以上 | 旧版本对STM32N6支持不完整 |
| 芯片Pack | STM32N6xx_DFP最新版 | 必须包含N647的FLM算法 |
| STM32CubeMX | 6.10以上 | 用于生成External Loader工程 |
| STM32CubeProgrammer | 2.15以上 | 用于烧录和校验 |
这个清单里的每一项都值得你花两分钟确认,尤其是芯片Pack。STM32N6系列是新产品,如果Pack包停留在早期版本,外部Flash的算法文件可能根本没有被包含。我习惯直接通过Keil的Pack Installer更新到最新版本,同时注意STM32CubeMX要能识别到MX25UM51245这颗Flash,如果CubeMX的Flash列表里找不到这个型号,后面生成配置代码时会有麻烦。
4.2 Keil MDK完整配置步骤
重点来了,讲一下完整的配置过程。假设你已经用STM32CubeMX初始化好了OSPI外设,并且能够正常操作外部Flash的读写(也就是内存映射模式能跑通),那么配置Keil的下载算法只需要五步。
第一步,打开Options for Target,切到Utilities选项卡,勾选“Use Debug Driver”,确保调试器和烧录使用的是同一个接口。Flash Download页面的“Download Function”勾选Erase Sectors、Program、Verify,Reset and Run看需求勾选。
第二步,点击Flash Download选项卡里的Add按钮,在弹出的算法选择对话框里找到MX25UM51245对应的条目。如果你的Pack安装正确,这里能直接找到名字类似MX25UM51245G STM32N647的FLM文件。如果找不到,点“Add”对话框下方的“Browse”手动定位FLM文件。FLM文件一般在Pack安装目录的ARM\Flash下,文件名可能是MX25UM51245G.FLM。
第三步,添加完成后,选中这个算法条目,在下方配置它的起始地址和大小。MX25UM51245的容量是64MB,也就是0x4000000字节。如果外部Flash接在OSPI的Bank1,起始地址通常配置为0x70000000。如果接在Bank2,则是0x90000000。
第四步,回到Target选项卡,确认片上Flash的起始地址和大小没有因为添加外部Flash而改变。STM32N647的内部Flash地址是0x8000000开头,外部Flash地址不能和它重叠,否则Keil会拒绝生成下载文件。
第五步,重新编译整个工程,再次点击Download按钮。如果一切正常,Keil的输出窗口会显示下载进度条,先加载外部Flash算法到RAM,然后擦除指定扇区,写入固件,最后执行校验。
4.3 验证烧录与回读校验
烧录成功之后别急着高兴,做一次完整的数据回读校验。把固件的大小和RAM里保存的原始二进制对比一遍,确认写入外部Flash的数据和预期完全一致。更保险的办法是用STM32CubeProgrammer的Read功能,把外部Flash的内容读出来保存成hex文件,然后用文本对比工具和原固件对比。
我在实际项目里会额外做一次“掉电重启运行测试”。把启动模式配置成从外部Flash启动,整板重新上电,如果程序能在没有调试器干预的情况下跑起来,才说明外部Flash的下载和启动链路彻底通了。这一步能发现一个很隐蔽的问题——FLM算法写入的数据是正确的,但启动配置里外部Flash的映射地址不对,导致重启后CPU找不到代码入口。
5. 报错变体与排查速查表
5.1 常见报错变体对照
我在各个技术社区里看到过不少类似的求助帖,报错文字略有差别,但根因大同小异。整理成表格方便对照:
| 报错信息 | 大概率原因 | 优先级排查方向 |
|---|---|---|
| failed to create the download file for mx25um51245 external flash | FLM文件缺失或路径错误 | 检查Programming Algorithm列表 |
| Cannot load flash programming algorithm | Pack未安装或版本过旧 | 更新STM32N6xx_DFP |
| Error: Flash Download failed - "Cortex-M55" | 调试器连接不稳定或时钟配置错误 | 降频OSPI CLK,检查硬件连接 |
| No Algorithm found for: 70000000H | 外部Flash起始地址配置错误 | 核对Bank和地址映射 |
| Verification of Flash Programming Failed | FLM算法与Flash型号不匹配 | 确认FLM文件对应MX25UM51245G |
5.2 硬件层面的检查清单
如果软件配置都检查过了仍然报错,不要怀疑人生,回头看看硬件。首先用万用表测量MX25UM51245的供电电压,确认是1.8V而不是3.3V,这个错误特别容易犯。然后确认WP(写保护)引脚和HOLD引脚有没有通过10K电阻上拉到正确电平,这两根引脚在悬空状态下会莫名其妙地干扰Flash的SPI通信。
另外,检查OSPI的时钟线CLK和数据线IO0-IO7有没有物理短路。我调试过的板子里出现过IO2和IO3在PCB layout时走线相邻太近导致串扰的问题,表现为算法加载偶尔成功、偶尔报错的随机性故障。这种情况下,大概率就是硬件上的信号完整性问题,不要再去折腾软件了。
5.3 地址、映射与启动配置的量级认知
补充一个很多人容易忽略的概念:外部Flash的起始地址不是随便写的。STM32N6系列的OSPI外设支持多个存储映射区域,不同的Bank映射地址不同,而且地址范围很大,和Cortex-M内核的地址空间是对应的。如果你在Keil里给外部Flash配置的起始地址和STM32CubeMX里初始化OSPI外设时设置的地址不一致,下载算法写入数据时会写到内核根本访问不到的地方,结果就是“明明下载成功了,但程序还是跑不起来”。
还有一点值得提醒:MX25UM51245的容量是64MB,这个大小超过了Cortex-M4等老内核常见的4GB地址空间的直接映射范围吗?其实没有,64MB完全在映射范围内。但如果你在Keil里把外部Flash的Size字段填成了错误的数值,比如填了0x2000000(32MB),那么超过这个范围的固件就会写入失败,Keil同样会以“failed to create the download file”之类的方式报错。
我的习惯是在配置完外部Flash之后,用CubeProgrammer手动连接一次芯片,读取外部Flash的JEDEC ID,确认读到的ID是C2 2A开头的Macronix厂商ID。如果读出来的ID不对,就说明外部Flash通信链路本身有问题,这时候在Keil里再怎么折腾FLM文件都是无用功。
6. 一些容易踩的坑和我的习惯做法
6.1 生成外部Loader工程的注意事项
如果你决定向STM32CubeProgrammer的External Loader方向走,而不是只用Keil内置的FLM文件,就要注意External Loader工程的创建细节。在STM32CubeMX里选择芯片型号后,在Middleware和Software Packs里启用External Loader支持,这时CubeMX会要求你选择对应的Flash型号。如果列表里没有MX25UM51245,你可以手动添加一颗容量和接口类似的Flash,然后修改生成的代码参数。
External Loader工程编译出来的是.ldr文件,不是普通固件。编译的时候不能使用微库(MicroLIB),必须使用标准C库,否则生成的Loader在Keil和CubeProgrammer里加载时会出问题。另外,编译器的优化等级不要开到最高,有时候优化会把初始化时序的关键代码优化掉。
6.2 借助J-Link的独立烧录方式绕过故障
如果你手头有J-Link调试器,还有一个备选方案:使用J-Link Commander(JLink.exe)配合J-Link的Flash下载算法。J-Link对MX25UM51245这种常见的OctalSPI Flash通常有独立的算法支持,而且J-Link的下载算法和Keil的FLM机制是独立的,有时候反而能绕过Keil的配置问题。
J-Link的方式是这样的:打开JLink Commander,连接STM32N647目标板,执行loadbin命令加载固件二进制文件,指定起始地址为外部Flash的映射地址,J-Link会自动调用内部算法完成下载。这个方法不依赖Keil的工程配置,用于验证“到底是Keil配置的问题还是硬件的问题”非常高效。
6.3 我的排查顺序,每次都管用
最后分享一个我个人的排查顺序,遇到这个报错时按顺序走,能省下很多白折腾的时间。第一步先测硬件,Flash供电、片选、时钟线,确认硬件链路正常。第二步用J-Link或CubeProgrammer读取Flash ID,确认通信没问题。第三步查Keil的FLM算法有没有正确加载,路径对不对。第四步检查初始化代码里的时钟配置,把OSPI时钟降下来试一次。第五步重新生成工程,从头配置一遍。
这套顺序的核心逻辑是:先确认硬件能通信,再处理软件配置,最后才怀疑算法文件本身。绝大多数情况下,这个问题都不是FLM文件损坏,而是某一个环节的配置没有对齐。
MX25UM51245这颗Flash配合STM32N647Z0,在外部存储扩展和AI模型部署场景里是很好用的一套组合,下载算法的配置问题解决好之后,后面开发会顺畅很多。如果你在配置过程中遇到了这个报错的其他变体,或者有其他经验想补充,也可以继续交流。