简介:一份面向嵌入式入门读者的 MDK Flash 算法资料包,以 STM32H743 平台和 W25Q64 SPI Flash 为主线,覆盖下载算法实现、HAL/寄存器两种工程写法以及 LED 基础例程,帮助新手理解 MDK 下 Flash 编程算法与工程配置的核心脉络。作者按“只展示最核心原理”的笔记思路整理,源码、脚本与说明互相配合,适合刚接触 MDK、想弄清下载算法生成与烧写流程的读者对照学习。包内共 1426 个文件,包含 518 个 h 头文件、363 个 c 源文件、292 个 icf 链接脚本、121 个 s 汇编文件,另有 ld/sct 链接描述文件、flm 算法文件、hex 固件和 keilkilll 批处理脚本,整体仅 14.89MB,工程目录划分清晰,便于直接打开和检索。已有 1038 人浏览学习。通过这份资料可拿到可直接编译的 STM32H743 LED/W25Q64 例程、Flash 算法相关源码与清理脚本,并借助作者提炼的核心原理笔记快速建立存储器配置与下载算法的整体认知。 搞嵌入式开发这些年,我最大的一个感受就是:下载程序这件事,看着简单,翻车的时候真能把人逼疯。明明编译零报错、仿真器识别正常、芯片电源也是好的,但一点Download就是给你报错。上周帮朋友排查一个STM32F103C8T6下载失败的问题,折腾了三个多小时,最后发现罪魁祸首居然是MDK里的Flash Algorithm(Flash算法)选错了。这事让我决定把Flash算法整个研究明白,顺手把常用的算法文件整理成了MDKflashAlgorithm.rar。这篇文章我就从原理讲到实操,把Flash Algorithm是什么、怎么选、怎么配,以及我踩过哪些坑,一次性说清楚。不管是刚入门的小白,还是玩Bootloader和外部Flash的老手,这篇文章都有值得你看的内容。
1. Flash Algorithm到底管什么事
1.1 为什么下载程序必须靠它
很多人第一次接触Flash Algorithm是在Keil MDK的下载配置界面里,看到一长串类似STM32F10x Med-density Flash 128K的选项,根本不知道这是干嘛的,乱选了一个,稀里糊涂就下载成功了;等到换芯片或者用外部Flash的时候,就各种报错。
先讲一个最基础的问题:为什么下载程序必须依赖Flash Algorithm?因为芯片内核(Cortex-M系列)通过SWD或者JTAG调试接口连上仿真器之后,调试接口本身是没法直接给Flash写数据的。Flash控制器有自己的一套时序和状态机,必须由CPU执行一段特定程序,去操作Flash控制器的寄存器,才能完成擦除和写入动作。但问题来了:CPU的代码从哪里执行?Flash还在擦着,没法执行Flash里的程序,所以MDK的做法是把一段小程序先加载到RAM里,让CPU跑这段RAM里的程序去控制Flash控制器。这段运行在RAM里的、专门负责擦写Flash的程序,就是Flash算法。
你可以理解为:CPU是室友,Flash是快递柜,调试接口只是门禁卡能让你进门,但想从快递柜里取东西,得先把取件密码(Flash算法)塞给室友,让他去操作柜子。没有密码,你干瞪眼也没办法。
1.2 一次真实的下载失败排查
我在帮朋友排查那个STM32F103C8T6问题时,现象非常典型:ST-Link能正常识别到芯片设备ID,Keil也能正确检测到内核,可一点Download,立刻弹出Flash Download failed - Target DLL has been cancelled。刚开始我怀疑是接线不稳、供电不足,换线、换USB口、量电压,都没用。芯片本身还是新的,BOOT0接地,复位电路也没问题。
折腾到后面我才反应过来,他新建工程的时候Project设置里选的是STM32F10x High-density Flash 512K这个算法,但C8T6芯片的实际Flash大小只有64K。高密度算法的扇区映射和擦除指令,跟Med-density的芯片对不上,擦除地址越界,算法直接失败。换成STM32F10x Med-density Flash 128K之后,一次下载成功。
这个案例特别典型。STM32F1系列根据Flash容量分了好几个密度等级,算法必须严格对应。很多下载失败、程序烧完不对的问题,追根溯源都是算法没配对。从那之后,我每次给新板子写程序,第一步不是看代码,而是先确认Flash Algorithm选对了没。
2. FLM文件内部的秘密
2.1 算法文件的构成模块
Keil MDK里用的Flash算法,实际是后缀为.FLM的文件,存放在MDK安装目录的ARM\Flash文件夹下。这个文件本质上是一个可以加载到RAM里执行的程序镜像,再加上一些描述信息头。描述信息里记录了算法名称、设备类型、Flash起始地址、页大小、扇区大小等关键参数,MDK靠这些信息决定怎么调度算法。
一个完整的FLM算法文件内部至少有这样几个函数模块:
| 函数名 | 作用 | 调用时机 |
|---|---|---|
| Init | 初始化Flash控制器时钟、接口、GPIO等 | 每次下载开始时调用 |
| UnInit | 反初始化,关闭Flash控制器电源或恢复状态 | 下载结束或出错时调用 |
| EraseChip | 整片擦除,清空全部Flash内容 | 用户选择“整片擦除”时调用 |
| EraseSector | 擦除单个扇区 | 按扇区擦除时反复调用 |
| ProgramPage | 写一页数据到Flash | 下载数据时重复调用 |
| Verify | 校验写入的数据是否正确 | 下载完成后可选调用 |
这些函数全都在RAM里执行,因为擦写Flash时,Flash本身的内容正在被改动,不能从Flash取指令。不同芯片的擦除粒度不同,比如F1系列一个扇区大小随密度等级变化,F4系列还有不同Bank和扇区大小。算法文件里必须把这些参数写对,MDK才能正确计算地址和调用擦写流程。
2.2 MDK的下载流水线
我大概梳理一下MDK点下Download之后发生的事,理解了这条流水线,后面排查问题会非常有方向感:
- 仿真器连接目标芯片并复位,让CPU停在复位状态。
- MDK根据你在配置界面里选择的FLM文件,把算法加载进指定的RAM区域。
- CPU复位后开始执行RAM里的Init函数,初始化Flash控制器。
- 根据配置执行整片擦除或按扇区擦除。
- 把编译好的代码按页写入Flash,每写完一页如果有校验就立即读取对比。
- 全部写完后,执行UnInit,然后复位芯片,跳到0x08000000开始运行。
所以很多人下载失败后发现芯片里的程序全没了,就是因为下载流程里通常带了擦除动作。如果你选择了“Erase Full Chip”,哪怕后面的编程步骤失败,芯片内容也已经被清空了。这个在调试Bootloader的时候尤其要注意,我在后面会单独讲。
3. 实操:在MDK里把Flash算法配明白
3.1 配置面板与核心参数
Keil MDK里配置Flash算法的入口有几个,最常用的是:Options for Target(快捷键Alt+F7)→Utilities标签页 →Settings按钮,弹出的Flash Download界面里,就是所有和下载编程相关的配置。
在Programming Algorithm区域,你会看到当前工程已经添加的算法列表。点击Add可以选择更多算法,选中一个算法后可以修改Start地址和Size大小。比如STM32F103C8T6的Flash起始地址是0x08000000,大小是0x00010000(64K)。这里的地址和大小写错,轻则下载报错,重则烧完程序直接跑飞。
下面还有个RAM for Algorithm设置,默认是一段固定的RAM区域,用来承载算法程序运行时的堆栈和数据。比如F103配置的是0x20000000起始,大小0x1000。如果你的RAM很小,或者算法本身很大,这里内存不够也会导致加载失败。有些国产芯片项目里,这个问题尤其常见,后面第4小节我会展开。
另外很多教程都会让你在Debug标签页的Settings里做同样的配置,其实两个位置是同一套底层设置,改哪里都行。新手记一个入口就够,不要两边改混了。
3.2 新板子、国产芯片怎么找算法
如果你用的是ST的芯片,MDK默认安装的算法列表基本能满足99%的需求。但这两年国产芯片用得越来越多,GD32、AT32、华大HC32、极海APM32这些芯片,有的可以直接借用ST同型号的算法,有的必须用厂家自己的算法,否则擦除和编程会不稳定,甚至把芯片锁死。
以GD32F103系列为例,它内部Flash架构和STM32F103高度相似,所以也能用STM32F10x Med-density Flash 128K这类算法。我在实际项目中就这么干过,正常下载没问题。不过GD32官方文档里明确建议使用自家提供的Flash算法,因为个别型号的页大小、扇区大小和ST并不完全一样,长时间、大批量烧录时可能遇到偶发失败。
国产芯片厂商一般都会在自己的Pack包或者开发板资料包里附带FLM文件,按照README说明拷贝到ARM\Flash目录,或者直接双击Pack包由Keil自动安装,重启MDK就能在算法列表里看到。如果你找不到官方算法,又必须临时用兼容算法烧录,建议先做一次全片擦除再下载,不要用默认的增量擦除,能减少不少兼容性问题。
3.3 外部SPI Flash与Bootloader场景
经常有人问:我想把代码下载到外部SPI Flash,比如W25Q64里面,MDK怎么配?答案是必须有一个专为这颗外部Flash写的FLM算法。因为这个算法要初始化SPI接口、发送读写命令,而不是操作芯片内部的Flash控制器。市面上常见的做法是用J-Link配合Segger的SPI FlashLoader,或者在MDK里加载第三方提供的SPI FLM算法。
还有一个高频场景是RT-Thread、STM32裸机Bootloader开发:Bootloader放在内部Flash起始地址,App放在偏移之后的地址。这时候你需要在MDK里把App工程的IROM1起始地址改成偏移后的地址,比如0x08008000,大小同步减小。下载App时,Flash算法仍然选芯片内置Flash的算法,但要注意Start地址必须按你工程实际需求配置,别把Bootloader区域覆盖掉。如果只是下载App,最好选择Erase Sectors而不是Erase Full Chip,否则每次下载都会把Bootloader一并擦除,连串口的升级引导能力都没了。
顺带提一句关于生成bin文件的热搜技巧。Bootloader和App方案里,经常需要单片机制作OTA升级固件,标准做法是在Options for Target→User标签页的After Build/Rebuild里加一行:
fromelf --bin --output=.\Objects\app.bin .\Objects\app.axf这样编译完成后自动生成bin文件,配合Bootloader的串口或者网络升级逻辑非常方便。别小看这一步,很多新手都是靠这句命令摸到bin生成门道的。
4. 那些年踩过的坑
4.1 下载成功但程序就是没反应
有个朋友遇到过这种情况:程序下载过程没有任何报错,进度条跑完,但板子就是没反应,按复位也没用。最后发现是他在Flash Download界面勾选了Reset and Run,但芯片在上电后的启动模式或者Boot引脚配置和预期不符,导致复位后没有从Flash启动,而是进入了系统存储器Bootloader。
这个问题的排查思路不是看Flash算法,而是要确认Boot0引脚的电平。对STM32来说,Boot0拉高会从系统存储器启动,很多用USB转串口自动下载电路开发的板子,Boot0会被上位机自动拉高,下载完如果不拉低,复位就进不了用户程序。我建议调试阶段不要过分依赖Reset and Run,手动复位观察启动行为更可靠。
另外也是隐藏较深的一个点:有些国产芯片的FLM算法支持ProgramPage函数,但页大小和Keil识别出来的不一致,编程时出现最后一段代码写不进去、编译通过的代码下载完总是缺尾巴的症状。遇到这类情况,去芯片官网找最新的FLM文件通常能解决。
4.2 算法加载失败和RAM溢出
下载时报Cannot load flash programming algorithm,这是另一个高频错误。这个报错直译就是“无法加载Flash编程算法”,原因多数是RAM for Algorithm设置的内存区域不够用,或者算法被加载的RAM地址和芯片实际SRAM地址不对应。
我调试一颗小容量RAM的芯片时,默认算法需要0x2000字节的RAM空间,但芯片SRAM总共才8K,我又在分散加载文件里把前4K都分配给了业务数据,算法加载时发现区域重叠或者空间不足,直接加载失败。解决办法是把RAM for Algorithm改成一段没有使用的SRAM地址,并适当调大Size;如果整个SRAM都很紧张,那就得优化工程的RAM占用,给下载算法留出生存空间。
在Flash Download界面里,如果有多个算法同时存在,比如既有内部Flash算法又有外部SPI Flash算法,要注意顺序。MDK默认按列表顺序执行,先擦除编程了内部Flash,再处理外部Flash,顺序反了可能导致内部Bootloader被先覆盖,而外部固件还没写进去,板子陷入半砖状态。
4.3 Keil4、Keil C51、MDK同机共存的坑
再聊聊热词里那类“同一台电脑可以安装Keil 4和Keil C51和MDK吗”的问题。我的经验是:可以,但要注意安装顺序和目录规划。
Keil C51和MDK(原Keil MDK-ARM)可以共用同一个安装根目录,它们只是不同的工具链,用同一个IDE壳子。先装C51再装MDK,通常能自动识别共用数据目录;反过来装,C51的编译工具链偶尔会注册不到,还得手动补。Keil 4和Keil 5本质上是两个大版本,实际中不少人会装双版本,但个人建议优先用Keil 5,只有维护老项目时才保留Keil 4。
还有一点,不同版本的MDK安装的FLM算法文件路径可能不一样,换版本之后原来配置好的算法可能找不到了。升级MDK前,把自己项目里用到的FLM文件备份一份是最稳妥的。这正是我整理MDKflashAlgorithm.rar的动机之一:版本升级、换电脑,文件一拷就完事,不慌。
5. 手把手整理自己的Flash Algorithm库
5.1 目录规划与文件管理
为什么要专门整理一个Flash算法压缩包?因为MDK自带的FLM文件分散在ARM\Flash目录里,还有不少是装在Pack包里、由版本管理工具拦着的,你换了新电脑或者给同事同步工程时,经常出现缺算法文件的情况。我自己的习惯是按厂商和芯片系列分目录维护:
MDKflashAlgorithm/ ├── ST/ │ ├── STM32F0/ │ ├── STM32F1/ │ ├── STM32F4/ │ └── STM32H7/ ├── GD32/ ├── AT32/ ├── HC32/ ├── APM32/ ├── SPI_Flash/ │ ├── W25Q64/ │ └── W25Q128/ ├── README.md每个目录里放对应厂商官方发布的FLM文件,来源我都记在README里。这样不管是新项目选型还是老项目维护,我都能快速找到对应算法,不用反复去官网翻下载链接。尤其是做了一个基于外部SPI Flash的UI固件项目之后,那个SPI FLM文件根本不在Keil默认安装里,全靠备份才好几次在换电脑时保住环境。
5.2 使用方法与团队共享建议
这份算法库的使用方法很简单:有正确安装路径就直接把FLM文件复制到MDK安装目录的ARM\Flash下,重新打开工程就能在算法列表里看到;有厂商Pack的,优先用Pack安装,避免手动覆盖造成版本混乱。如果多个厂家都提供了同名FLM文件,建议以芯片型号和日期后缀区分,别直接覆盖默认文件。
团队开发时,我有几个实用习惯可以分享:一是把FLM文件纳入版本管理,不带进仓库的个别文件很容易变成“只在老张电脑上能下载”的玄学问题;二是每个芯片配套的烧录配置参数,写在项目README里,比如算法名、Start地址、RAM for Algorithm等,减少沟通成本;三是每季度或版本大升级时,定期备份一次ARM\Flash目录,压缩包体积不大,但关键时刻能救命。
5.3 一个值得养成的下载前检查清单
根据个人经验,我总结了一套下载前30秒检查清单:先看工程选项里的Flash算法是否和芯片型号匹配;再确认Start地址和Size是否覆盖实际代码区域;接着检查RAM for Algorithm是否和分散加载文件冲突;最后决定用整片擦除还是扇区擦除。这一套流程下来,至少能挡掉七八成的下载类问题,剩下的就事论事再看调试器和硬件连线。
我在实际项目中体会到,Flash算法配置从来不是什么高深学问,但它就是下载成功与否的命门。很多人在网上问“为什么下载不了”,其实问题就出在这个容易被忽略的小选项上。花半小时把算法原理和配置方法搞清楚,往后的开发日子能省下无数个查报错的夜晚。这份MDKflashAlgorithm.rar,覆盖了我手头80%的开发板算法需求;剩下20%的冷门芯片,就是靠文章里这些排查思路现学现配的。希望这篇整理也能让你的调试之路顺利一些。
本文还有配套的精品资源,点击获取