ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Keil Flash Download failed故障排查:从连接配置到固件恢复

Keil Flash Download failed故障排查:从连接配置到固件恢复 1. 先看懂报错被Flash Download failed掩盖的四种真实故障晚上十一点板子第三次在点击LOAD按钮后弹出血红色的Flash Download failed - Target DLL has been cancelled我当时的第一反应是换USB线、换USB口、重装Keil MDK折腾到凌晨两点问题原样。后来我才意识到这类报错根本不是单一故障Keil把不同阶段发生的连接问题全都归拢到了这一句话下面如果不先搞清楚是哪一阶段的失败后面所有操作都是在瞎试。Target DLL has been cancelled这句话里的DLL指的是Keil去调用调试器厂商提供的动态库比如ST-Link的STLinkUSBDLL.dll、J-Link的JLinkARM.dll。整个下载流程是Keil点LOAD后先初始化DLLDLL再去枚举USB设备、和目标芯片建立SWD/JTAG连接然后加载Flash算法、擦除、写入、校验。如果DLL在“和目标芯片建立连接”这一步卡住或者超时Keil就会取消整个下载会话最终抛出DLL has been cancelled。所以凡是驱动异常、调试器固件不匹配、线没接对、目标板没供电这类问题几乎都会报这个错。而报错后面跟着的内核名称比如Flash Download failed - Cortex-M3含义又不一样。这说明DLL已经成功和芯片握上手了Keil甚至读到了Cortex-M3内核的相关信息但接下来加载Flash编程算法时出了问题。最常见的两个原因Debug页面里的芯片型号和实际芯片不一致Flash Download页面里没有添加和芯片匹配的Programming Algorithm。这一层故障比DLL cancelled更靠后排查方向完全不同。还有一种报错是could not load file ...\xxxx.axf这个基本和硬件无关纯粹是Keil找不到要下载的固件文件。要么编译失败没有生成axf要么工程路径里有中文或空格导致链接输出异常要么Output选项卡里的可执行文件名被改过。地址栏里那串01_freertos template路径本身就是个典型的中文命名空格工程名组合很多人是在这里栽的。我把这几年遇到过的、包括论坛上高频出现的报错变体整理成了一张排查优先级表方便按顺序对号入座报错信息关键片段最常见原因排查优先级Target DLL has been cancelled调试器驱动/固件异常、线缆接触不良、目标板供电问题最高Cortex-M3 / Cortex-M4 / Cortex-M0芯片型号选错、Flash算法缺失或类型不匹配高could not load file xxx.axf工程路径、编译输出、可执行文件名问题中Cannot access target / RDDI-DAP ErrorSWD引脚被复用、芯片进入低功耗、读保护开启中遇到报错第一件事不是重装Keil而是按这个顺序自查。我见过太多人因为一个驱动版本问题把整个开发环境卸载重装若干遍最后发现是ST-Link的USB驱动被Windows更新顶掉了。2. 硬件与连接上最容易翻车的三个环节2.1 调试器驱动和固件版本互相打架先说一个很多人没意识到的坑Keil MDK版本和调试器驱动之间是存在兼容性边界的。新版的Keil比如5.36以后集成的CMSIS-DAP、ST-Link调试组件比较新如果电脑上还残留着早期版本的ST-Link驱动或者装过ST-Link Utility又卸载不干净很容易出现“设备管理器里能看到ST-LinkKeil里就是连不上”的诡异状态。Windows驱动栈里多条版本记录共存时系统会优先加载不匹配的那个DLL初始化必然失败。这个问题的处理我试过最有效的方法设备管理器里找到ST-Link Debug右键卸载设备卸载时勾选“删除此设备的驱动程序软件”然后拔掉调试器、重启电脑、重新插上让系统重新枚举并安装正确驱动。如果装完后Keil还是报DLL cancelled去ST官网下载STM32CubeProgrammer用它的固件升级功能给ST-Link刷新一遍固件。注意ST-Link的板载固件和PC驱动是两套东西很多人只更新了驱动没给调试器升级固件或者反过来这两件事都得做一遍才能排除隐患。我自己的经验是每次接到新板子第一件事就是打开STM32 ST-LINK Utility在ST-LINK菜单里点Firmware update把板载固件刷到官方最新版本然后再打开Keil测试连接。这套流程能过滤掉大约四成的DLL cancelled问题。2.2 SWD接线、杜邦线和复位电路调试器和目标板之间的连接远没有想象中那么“随便”。SWD模式理论上只需要SWDIO、SWCLK、GND三根线但实际工程里我强烈建议至少再引一根NRST甚至把3.3V参考电平也接上。原因在于很多下载失败发生在“连接成功但擦除时卡死”这个阶段这多半是SWCLK信号在长线上衰减导致的。杜邦线超过20厘米或者线材质量一般SWD时钟稍微高点就出问题表现五花八门有时能读IDCODE但无法擦除有时擦除到一半报错有时直接DLL cancelled。如果你手里的板子是用杜邦线连调试器的试试把SWD时钟频率降低。Keil的Debug设置里进入Settings把Max Clock从默认的几MHz降到1MHz或更低很多“玄学失败”就这么解决了。我实际遇到过一块板子默认4MHz下擦写必失败降到1MHz后连续烧录几十次都稳如泰山。这个操作的成本几乎为零但很少有人第一时间尝试。复位电路也要注意。有些板子在NRST引脚上挂了较大的电容上电瞬间复位信号被拉长调试器发起连接时芯片还停在复位状态握手失败就报DLL cancelled。如果板子是批量买的核心板可以先看看原理图里复位电容是不是100nF以下大于这个值可以考虑飞线短接一下复位电容再测。还有一种更隐蔽的情况复位按键卡住没弹起来我甚至见过板子出厂时复位键被胶带粘住导致永远复位的案例。2.3 目标板供电不稳看不见的故障源调试器通过SWD接口虽然会输出参考电平但大多数板载调试器并不会向目标板供电或者只提供极小的电流。如果你用的是带调试功能的核心板板上一般有LDO会把USB的5V转成3.3V但一旦整板功耗偏高比如接了OLED屏、WiFi模块、电机驱动LDO输出就会被拉垮芯片供电电压跌到复位门槛附近结果就是下载过程中芯片反复复位Keil那边的DLL会话被中断报错毫无悬念。我排查这类问题有一个快速判断法拔掉所有外设模块只保留调试器、最小系统、电源再点一次LOAD。如果下载成功基本锁定是供电不足或外设干扰。这时候不要急着换更大功率的电源先看看是不是某个外设的电源引脚和核心板共用了同一条LDO输出有条件的话给外设单独供电。另外USB线本身也可能是个大坑。有些线只有充电能力没有数据线芯插上去Windows会提示未知USB设备还有的是线材内阻过大目标板和调试器一起从同一个USB口取电时电压被拉低。我抽屉里常备两根短线、粗线芯的USB线专门用来调试很大程度上就是为了避开这类供电问题。3. Keil工程里最容易漏掉的三处配置3.1 芯片型号必须和实际封装完全一致进入Options for Target对话框Debug页面的Use下拉框通常大家都记得选但真正容易漏的是Device页面里的芯片型号。很多人从旧工程复制过来或者用STM32CubeMX生成工程后没检查型号结果芯片实物是STM32F103C8T6Medium-density64KB FlashKeil里却选的CBT6或者ZET6这种型号不匹配在下载阶段就会体现出来Keil读到了Cortex-M3内核但下载算法找不到对应容量的Flash布局于是抛出Cortex-M3后缀的报错。选型号这个动作一定要精确到具体后缀因为STM32同系列不同后缀的Flash容量、甚至Flash扇区划分都可能不一样。就算Keil能识别内核烧录算法不匹配一样失败。一个典型的例子STM32F103C8T6是Medium-density下载算法应该选STM32F10x Med-density Flash 128K如果你手滑选了High-density 512K的算法下载时可能能擦除能写入但校验阶段大概率报错或者程序运行时各种诡异复位。这个小细节能让很多人卡半天。3.2 Flash Download页面里的Programming Algorithm缺失芯片型号选对了Flash算法列表为空或选错同样会报错。在Options for Target里切到Utilities或Debug设置里的Flash Download子页你会看到一个Programming Algorithm列表正常的工程里应该有一行对应芯片的FLM算法文件。如果这个列表是空的或者只列了一个和当前芯片不符的算法下载必失败。遇到列表为空点Add按钮从弹窗里找到与芯片匹配的算法。注意弹窗里每个算法名称都直接对应一种Flash类型和容量范围比如STM32F10x High-density Flash 512K和STM32F10x Med-density Flash 128K是两条不同的算法。还有一种情况是算法列表里压根没有你需要的型号这时候大概率是Device Family Pack没装全。去Keil官网的Pack Installer里找到对应厂商的Device Pack装上后再回来看算法列表就全了。这个问题在新建工程、或者别人分享的工程里特别常见很多开源工程作者自己用的算法配置没跟着工程文件走。3.3 Utilities与Debug两处设置没有同步改这是新手最容易忽略、老手也容易犯的低级错误。Keil里和下载相关的设置入口有两个一个是Debug选项卡负责调试会话连接另一个是Utilities选项卡负责Flash下载这两个页面的右上角都有一个Use下拉框可以选ULINK、ST-Link、J-Link、CMSIS-DAP Debugger等。很多人把Debug页面的调试器选成了ST-Link但Utilities页面还停在默认的ULINK结果点LOAD后Keil去调用ULINK的DLL而ULINK根本没接报错自然就是DLL cancelled。所以每次切换调试器型号务必同时检查这两处。Utilities页面还有个“Settings”按钮点进去后里面同样有Flash Download的算法配置入口和Debug页面里的是同一套配置改了一处另一处会联动但前提是右上角选择的调试器必须要和Debug页面一致这个联动才有意义。我的习惯是每次拿到新工程先把这两个页面的Use下拉框截个图对比一下确认完全一致再编译下载省掉很多莫名其妙的错误。3.4 工程路径、编译输出与axf加载失败could not load file ...01_freertos template\01_freertos template.axf这种报错本质是Keil在下载前去找axf文件时扑了个空。axf是ARM编译链接器生成的调试格式可执行文件Keil烧录时默认加载它。以下四种情况最容易触发工程放在中文路径或带空格的目录里某些版本的armcc对路径解析不友好链接阶段就出问题Output选项卡里勾选了Create HEX File但没勾选Debug Information导致axf不输出或输出不完整编译没成功却直接点了LOADaxf文件不存在修改了Output选项卡里的Name of Executable但Flash Download配置里还是旧文件名。我把工程统一放在D:\Projects\STM32\这类纯英文根目录下之后这类报错几乎绝迹。如果你已经按照上面的顺序检查过硬件、驱动、芯片型号、算法列表还是报DLL cancelled那也值得回头看一眼路径——有时候路径问题不会直接报could not load file而是表现为Keil在连接阶段异常退出看起来像是DLL问题实际是加载目标文件时就中断了。这里还有个容易被忽视的点User选项卡里如果有烧录前调用fromelf或者copy命令的步骤命令里的路径带中文或空格同样会让整个下载流程在启动阶段就失败表现也是DLL cancelled。4. 芯片锁死的典型场景与判别方法4.1 读保护RDP开启后的连接困境如果你的程序里主动操作过选项字节开启了RDPRead ProtectionLevel 1芯片的调试口默认就不允许SWD连接了Keil点LOAD会直接报错有时候是DLL cancelled有时候是Cortex-M3后缀的失败。很多人第一次遇到是在跑ST官方或者某些RTOS的例程时例程里有擦除选项字节或设置读保护的代码下载一次成功第二次就再也连不上了这时候十有八九是RDP被打开了。判断方法用STM32CubeProgrammer连接它通常能识别到芯片但提示读保护状态或者能在连接时复位/恢复选项字节。如果你手头只有Keil可以试试在Debug设置里把连接模式改成under Reset再点LOAD看能否绕过用户代码重新连上。但注意RDP导致的无法连接靠普通下载是无法解决的必须在CubeProgrammer或ST-Link Utility里执行Option Bytes级别的操作把Level降回0这个过程会触发全片擦除。后面固件恢复章节会写具体操作。4.2 SWD引脚被程序复用成GPIO这是单片机开发里一个特别经典的“自杀式操作”为了省电或者复用引脚在初始化阶段把PA13/PA14默认的SWDIO/SWCLK配置成普通GPIO或者模拟输入程序一旦跑起来调试口当场报废。最要命的是如果你把这段代码放在上电后立即执行的位置芯片每次上电都会在极短时间内废掉SWDKeil根本来不及建立连接于是报DLL cancelled或Cannot access target。我之前调试一块低功耗产品时干过这事为了省微安级电流把SWD引脚全部配成了输入浮空然后随手把程序下载进去第二次就再也连不上了。当时经验不足还以为芯片烧了换了一块坏一块最后才意识到的确是代码把调试口关了。解决办法就是下一章讲的“复位接管”模式芯片在复位期间是不执行用户代码的SWD在这个窗口期仍然可用只要能让芯片保持在复位状态调试器就能先连上然后在复位释放的瞬间立刻接管芯片、擦除Flash。另外如果板子允许也可以拉高BOOT0让芯片从系统存储器启动绕开用户代码再用串口ISP把Flash清掉。4.3 进入低功耗模式后调试会话失效芯片进入STOP或STANDBY模式后内核时钟停止调试器和内核的同步机制就断了。如果程序里设了“开机几秒后进低功耗”而你恰好在那几秒之后才点LOAD同样报错。这种问题的隐蔽之处在于芯片不是真的坏上电瞬间去下载也许还能连上过了窗口期就彻底没反应了。排查这类问题的时候可以先把电源断开用手按住复位键不放然后再上电、再点LOAD保持复位键按着的状态下尝试下载。因为芯片处于复位状态时是不会跑用户代码的自然也不会进低功耗。如果这样能连上基本确定就是低功耗代码把调试会话搞死了。更稳的解法是进入Debug设置把Connect模式改成under Reset让Keil每次都从复位状态接管芯片用户代码跑不跑得起来另说至少能先抢到控制权。4.4 快速判断“锁死”还是“硬件故障”锁死和硬件故障的表现很多时候长得一模一样点击LOAD以后Keil报连接失败。要快速区分最直接的土办法是把目标板NRST引脚拉低再试一次连接。如果能连上或至少能识别IDCODE说明芯片本身活着只是用户代码或者选项字节把调试口堵住了如果NRST拉低后依然完全无响应再考虑硬件层面比如晶振没起振、电源异常、芯片虚焊。另一种判别思路是用示波器量NRST引脚电压。正常情况下复位释放后NRST应该被上拉到高电平。如果NRST电压不稳定、在复位阈值附近抖动说明复位电路有问题可能不是锁死而是芯片一直在复位循环中连接当然失败。如果NRST稳定高电平但调试就是连不上且拉低NRST再连接能成功锁死的概率非常大。我一般会用STM32CubeProgrammer配合Hot Plug模式做交叉验证它能做到“在不上电的情况下连接芯片”连接成功就能读到IDCODE这比单纯在Keil里试错要直观得多。5. 固件恢复完整操作从复位接管到全片擦除5.1 方法一Keil自带Connect under Reset模式适用场景程序把SWD引脚复用成了GPIO或者上电后立刻进入低功耗模式但芯片本身没开读保护。这是我最先尝试的方法步骤如下用杜邦线或飞线把目标板的NRST引脚和GND短接。如果板子有复位按键可以一直按住不放如果连出了复位引脚直接短接到GND更稳。打开Keil工程进入Options for Target - Debug - Settings在Connect下拉框里选择under ResetReset下拉框选择Hardware Reset。点击LOAD下载观察Keil输出窗口。如果连接成功就在Keil发起连接的那一刻松开复位引脚或断开NRST与GND的短接线。下载完成后芯片Flash里的旧程序已经被覆盖SWD引脚恢复默认功能后续正常下载即可。这个方法的原理是芯片在复位状态下不执行Flash里的用户代码调试器利用这个窗口先建立SWD连接然后复位释放的瞬间调试器趁用户代码还没接管引脚之前完成擦除和写入。实际操作中要注意节奏最好一个人操作左手按住复位短接处右手先点LOAD眼睛盯着输出窗口看到“Connecting”或者进度条出现就松手。如果反复试都抓不住时机可以用一个更笨但更稳的办法把SWDIO那根线暂时断开先让调试器识别到目标板此时因为SWDIO断开连接会卡在等待状态然后再插上SWDIO同时让复位引脚释放这个技巧在老旧板子上成功率挺高。实践中还有一个小技巧在Debug设置里把Max Clock临时降到1MHz复位接管模式下时钟越低越容易抓住复位窗口因为握手时序变宽了。烧录成功后再把频率调回去就行。5.2 方法二STM32CubeProgrammer的Under Reset与Hot Plug模式如果Keil里反复抓不住复位窗口或者你手里的板子复位引脚没引出、无法手动拉低NRST那就换STM32CubeProgrammer。这个工具的连接能力比Keil里的CMSIS-DAP协议栈更暴力而且提供了独立的模式选择专门针对锁死芯片设计。打开STM32CubeProgrammer右上角选择ST-LINK然后在Mode下拉框里有几个选项Under reset和Keil的Connect under Reset原理一样由调试器控制复位信号需要目标板的NRST连到ST-Link的NRST引脚。如果你的ST-Link和板子是集成在一起的这一步能自动完成非常省事。Hot plug不依赖复位信号直接尝试连接。适用于芯片已经进入低功耗但调试口还没完全关掉的情况。Normal常规连接模式锁死状态下基本没用。操作流程选Under reset模式Frequency可以先选低一点比如4MHz然后点Connect。如果连接成功ST-LINK会弹出一行提示告诉你当前芯片的IDCODE、Flash大小等信息。这时候切到左侧的Erasing Programming页面选择Full chip erase执行全片擦除。擦除完成后芯片的选项字节也会重置RDP读保护会被清除。有一点要提醒全片擦除是“清场”操作芯片里所有用户代码、数据、选项字节都会丢失。如果之前的程序里有出厂校准数据或者蓝牙配对信息擦除后需要重新写入。对单片机开发来说这个代价通常可以接受总比板子变砖强得多。如果你连接的芯片设置了RDP Level 1CubeProgrammer连接时可能会提示读保护状态并询问是否要解除保护。这个操作本身就会触发mass erase所以如果芯片里的数据对你很重要先想想有没有备份。最终界面会有红色提示“Read protection is enabled”别慌按提示操作即可。5.3 方法三BOOT0拉高走串口ISP擦除这个方法适用于完全没有SWD接口可用、或者复位接管完全失败的极端情况。STM32全系列都带一个内嵌的Bootloader通过拉高BOOT0引脚再上电芯片会从系统存储器启动执行出厂固化的串口下载程序。在这个状态下用户Flash完全没被加载SWD引脚也不会被用户代码占用我们可以通过UART把Flash擦干净。步骤如下断开目标板电源把BOOT0引脚从低电平跳到高电平大多数核心板是拨码开关或跳线帽直接拨到1即可。保持BOOT1为低电平。用USB转串口模块把TXD接到目标板的USART1_RX通常是PA10RXD接到USART1_TXPA9GND共地。目标板上电此时芯片进入Bootloader模式。打开STM32CubeProgrammer右上角切换到UART模式选好串口号和波特率默认115200点Connect。此时通常不用设置引脚复位因为芯片已经在系统存储器里了。连接成功后同样执行Full chip erase或者只擦除需要的扇区。擦除完成后断电把BOOT0跳回低电平重新上电。此时芯片回到正常的Flash启动模式SWD调试口恢复再用Keil下载新固件即可。这个方法最大的价值在于它不依赖调试器只要能想办法把BOOT0拨高哪怕SWD引脚已经被焊死、芯片锁死都有机会救回来。我手头有一块老开发板ST-Link接口早就虚焊了后来全靠BOOT0串口方式维护固件实测非常可靠。需要注意的是串口ISP模式下某些芯片对波特率有要求如果连接不稳定把波特率降到9600试试。5.4 恢复后的防线把调试口保护写进代码规范经过一整轮擦除和重新下载问题解决了但更值得反思的是怎么避免下次再锁死。我现在的做法是在工程里约定两条规则几乎杜绝了这类问题的反复第一条凡是涉及SWD引脚的复用配置初始化代码里必须加一个“调试窗口期”。具体做法是上电后先延时500ms到1秒在这段时间里不初始化任何调试引脚所有外设时钟也不开。这样调试器每次都有充足的时间在复位接管模式下连接哪怕后面代码又把SWD配成GPIO至少留给开发一段可操作的窗口。延时结束后再用一个GPIO按键或者串口命令来确认是否进入用户程序如果检测到调试器在线就直接跳过引脚复用配置。第二条选项字节操作的代码放在独立的、需要显式注释才能加入编译的宏控制里。RDP、WRP这类操作人为误触发的概率太高了每次看到代码里有FLASH_OB_Unlock、FLASH_OB_EnableWRP之类的内容都要格外谨慎。就算真要开读保护也应该是在量产阶段的独立工程里由脚本控制而不是在主程序里随手一写。第三条所有批量生产的板子把NRST引脚引到测试点或者做成通孔焊盘。这一条看着不起眼但在现场复位接管、恢复固件的时候有没有一个方便接线的NRST引脚排查时间能差出几个小时。最后再分享一个我实测有效的习惯每次刷完自制固件我都会在下载完成后顺手做一个“Load后再次连接测试”——关掉Keil的下载窗口重新点一次LOAD确认还能连上。如果第二次连接也稳定说明芯片没有被程序锁死调试口还活着如果第二次就报错那说明新固件里有抹掉或者复用调试口的风险趁早回退代码别等下次需要调试时再抓狂。这个习惯帮我拦下了至少三次想当然的“上线看看”操作。
返回列表