用了一段时间Keil v5之后,很多人都会遇到同一个尴尬场景:手头明明有JLink,目标板供电、接线也都正常,但一点下载,Keil要么报“No J-Link found”,要么直接甩一句“The connected J-Link is defective”,甚至干脆在Device列表里找不到自己那颗芯片。这个坑我在自己装的JLink和几款非主流MCU上反复踩过,后来把整个链路梳理了一遍,发现核心问题就两个:一是Keil自带的Segger组件和自行安装的JLink驱动版本不匹配,二是Keil v5的设备支持机制从“内置数据库”变成了“Pack包”管理,官方没给你收录的芯片,就得自己想办法加。这篇经验帖主要解决这两件事,适合手里有便捷JLink、正在折腾STM32/GD32/自制Cortex-M板子、或者被Pack版本问题搞到崩溃的开发者参考。
1. 项目背景与核心问题拆解
1.1 Keil v5为什么不认自行安装的JLink
先说结论:大多数情况下,不是你的JLink坏了,而是Keil v5使用的JLink相关文件和你实际安装的JLink驱动不是同一个版本,两边“协商”失败,Keil就不认了。
Keil MDK v5安装之后,会在安装目录下的ARM\SEGGER文件夹里放一套精简版的Segger工具,包括JLinkARM.dll、JLink.exe、JLinkGDBServer等。这套文件是Keil发布时固定携带的OEM版本,版本号跟着Keil走,比如有些老版Keil 5.24内置的是JLink V6.xx的DLL,而你自行安装的Segger JLink驱动已经更新到V7.xx甚至V8.xx。真正连接时,Keil通过内置DLL去访问JLink硬件,然后JLink硬件里的固件又会和DLL做一次握手校验。只要DLL版本、固件版本、硬件版本三者之间有代差,握手就会失败。表现就是Keil里能看到“J-Link”字样,但一点连接就报错,或者说设备损坏。
这里还有个容易被忽略的点:Keil是32位应用程序,在64位Windows系统上跑的时候,它会加载32位版本的JLink驱动。而Segger官网新驱动安装后,默认路径下通常会有x86和x64两个子目录。如果你不小心把64位版本的DLL复制到哪里去了,Keil根本认不出来,照样报错。所以后面做DLL替换时,一定要记得取x86目录下的文件。
1.2 “添加自定义设备”到底在解决什么问题
Keil v5和Keil v4一个很大的差别,就是设备支持方式变了。Keil v4是在软件里内置一个设备数据库,选中芯片之后编译和烧录参数都写死在工程里。Keil v5改成了“CMSIS-Pack”机制,所有芯片的描述、启动文件、Flash算法、SVD调试描述文件,都以pack的形式存在。你在Pack Installer里看到的ST、NXP、GD32等厂商支持,本质上是下载了对应的.pack包。
这就带来一个问题:如果芯片厂商没有跟进Pack机制,或者你的芯片特别小众,或者干脆是你自己定制的SoC,那么在Pack Installer的Device列表里永远找不到它。但单片机还是要用的,程序还是要烧的。所以“添加自定义设备”本质上是在做三件事:
- 让Keil在新建工程时能选到这颗芯片,编译链接参数准确。
- 给Keil提供Flash算法文件(FLM),否则下载时Keil不知道怎么把程序写进Flash。
- 提供SVD调试描述文件,让调试器能解析外设寄存器。
很多人以为自定义设备只是改个名字,其实不改算法文件的话,就算你新建工程选到了名字,下载那一步照样会报“Cannot Load Flash Programming Algorithm”。这个坑我在后面专门展开讲。
2. 识别与驱动准备:先让自装JLink被电脑认出来
2.1 驱动安装与环境确认
拿到一个自装JLink,第一步不是急着打开Keil,而是先确认JLink本身能不能被电脑识别。把JLink插上USB口,打开设备管理器,看“通用串行总线控制器”或者“通用串行总线设备”下面有没有出现“J-Link”开头的设备。如果出现一个带黄色感叹号的未知设备,说明驱动没装好。这时候去Segger官网下载对应版本的JLink驱动安装包,装上即可。安装完之后,设备管理器里应该会显示类似“J-Link”这样的条目,没有感叹号。
然后打开JLink Commander。它在开始菜单里的名字可能是“J-Link Commander V6.xx”或类似叫法,也可以直接到Segger安装目录下找JLink.exe。打开后会提示输入目标设备型号,随便填一个你手里的芯片型号,如果连接成功,会打印出目标板的ID、内核类型、序列号等信息。这一步如果都能顺利通过,那说明你的JLink硬件、USB线、目标板接线都是好的,问题才能锁定到Keil这边。
这里有一个经验:USB线别用那种只能充电的线。JLink对USB数据线要求不算苛刻,但有些廉价线材要么接触不良,要么只有电源线没有数据线,插上去电脑没反应。我遇到过一次,换了根粗一点的线就好,折腾了半小时。
2.2 驱动版本怎么选才不踩坑
自行安装的JLink,尤其是非官方渠道的兼容设备,对驱动版本很敏感。新驱动往往对老硬件支持不友好,会出现“The connected J-Link is defective”这种提示。我的经验是分类型对待:
- 老款V8/V9兼容设备,尽量装6.8x到7.0x之间的驱动,太新容易报设备故障。
- 新款V10/V11设备,装7.x以上新驱动比较稳妥,老驱动反而可能出现无法识别固件的问题。
- 如果你不确定自己手里的JLink硬件版本,看JLink Commander启动时的版本号和固件提示,它会告诉你能不能升级。
这里顺便提一下,Segger新驱动会提示“Update Firmware”。对于兼容设备,升级固件有一定风险。不是不能升,而是升级前务必确认你手里设备的固件版本和驱动版本是配套的,否则升坏了反而更麻烦。所以我在后面所有操作里都建议:能不升固件就不升,优先用DLL替换和降级驱动来解决Keil识别问题,别动固件。
2.3 SWD和JTAG的接线规范
JLink和目标板之间,最常用的是SWD模式,最少只要四根线:VCC参考电压、SWDIO、SWCLK、GND。很多人图省事只接三根,不接GND,然后各种莫名奇怪的报错就来了。JLink的VTref就是通过VCC这根线检测目标板供电的,如果没接或者电压不对,Keil里会一直提示“Target power”相关问题。
JLink标准20Pin接口里,常用的几个引脚是这样:
| 引脚 | 名称 | 说明 |
|---|---|---|
| 1 | VTref | 目标板参考电压,接3.3V或5V,用于电平匹配检测 |
| 3 | SWDIO / TMS | SWD数据线,JTAG模式为TMS |
| 5 | SWCLK / TCK | SWD时钟线,JTAG模式为TCK |
| 7 | SWO / TDO | SWO跟踪输出,JTAG模式为TDO,调试时可选用 |
| 9 | GND | 公共地,必须接 |
| 13 | TDI | JTAG模式数据输入,可以不管SWD |
实际使用中,接1、3、5、9这四根线基本就能烧录。如果目标板没有标准20Pin接口,就从JLink的杜邦线单独引出来,把颜色对应好。我自己调试自制板子时习惯用四根杜邦线,省事、不容易接错。如果你发现能识别但下载不稳定,先检查一下SWCLK频率是不是太高,可以在Keil的Debug设置里把时钟降到1MHz左右试试,稳定了再往上调。
3. Keil v5识别自装JLink:DLL替换与版本匹配
3.1 Keil v5内置的JLinkARM.dll究竟是什么
Keil v5在ARM\SEGGER目录下放了一套自己的JLink动态库,Keil识别JLink时调用的就是这些DLL。这套东西和Segger官网驱动是两回事:官网驱动是给整个系统用的,谁都能调用;Keil内置DLL是Keil私有的,编译时绑定在Keil目录里。也就是说,你哪怕装好了官网驱动,Keil还是会优先用自己目录下的那套DLL去连JLink。
所以当Keil提示“No J-Link found”或者“defective”时,一种非常有效的方法就是把Keil内置的DLL替换成你自己安装的JLink驱动里的DLL。具体操作分四步:
- 先记录Keil安装路径。右键Keil快捷方式,打开文件所在位置,通常就是
C:\Keil_v5。 - 找到
C:\Keil_v5\ARM\SEGGER文件夹,把里面的JLinkARM.dll和JLink.dll(有的版本只有其中一个)复制到桌面备份。 - 到Segger官网驱动安装目录下,找到
x86子目录里的同名文件。把JLinkARM.dll复制到Keil的ARM\SEGGER目录,如果驱动目录里还有JLink.dll,一并复制过去。 - 打开Keil,重新进入Options for Target -> Debug,选择J-LINK调试器,点Settings,看能不能正常读取到序列号和目标板ID。
这里有几个细节。第一,Keil是32位程序,必须用x86目录里的DLL,x64目录里的文件复制过去基本无效。第二,替换前一定要备份,因为你不知道这次替换是否一定成功,万一版本不匹配,至少能退回去。第三,不同Keil版本对DLL名称的要求不一样,老版本主要认JLinkARM.dll,新版本已经转向JLink.dll,保险起见两个文件都替换掉。
3.2 替换之后Keil里的配置要点
替换DLL只是第一步,把Keil的调试配置也理顺了才算真正能用。打开Options for Target -> Debug,右边选中“J-LINK / J-TRACE Cortex”,点紧挨着的Settings按钮,会弹出一个窗口。这里重点看两个标签页:
Debug标签页里,如果设置正确,能看到你的JLink序列号、端口模式、目标设备内核。如果这里显示“Cannot find J-Link”或固件版本提示不匹配,回去检查DLL是不是放对了。在“Port”下拉框里,一般选SW或者JTAG。目标板用的是SWD就选SW,否则怎么都连不上。
Flash Download标签页更关键,这里对应烧录算法。很多人在网上找的工程模板打开之后,编译没问题,一烧录就报“Flash Download failed”,原因多半是这个页面里没有加对Flash算法。点“Add”,在弹出的列表里选中你目标芯片对应的Flash算法,比如STM32F103ZE对应的是“STM32F10x High-density Flash 512K”这样的条目。加好后,编程算法、起始地址、大小都会自动带出来。如果你的芯片是自定义设备,这个列表里可能找不到对应项,那就需要手动添加FLM文件,这个放到第4章细说。
还有一个很多人不注意的选项:“Reset and Run”。勾选它之后,烧录完成会自动复位运行程序;不勾选,下载完程序停留在停住状态。调试时我一般会勾上,少点一次复位。有时候目标板带有外部看门狗,烧录后立刻复位反而会被看门狗咬住不断重启,这种场景下就不勾,配合手动复位更可靠。
3.3 RDDI-DAP报错的个人处理经验
替换DLL解决了版本问题之后,下一个高频报错是“RDDI-DAP Error”。这个报错第一次出现时,我一度以为是JLink坏了,后来排查下来发现,它更像是目标板侧的连接问题,而不是DLL问题。
常见原因按概率排:
- 目标板没有供电,或者供电电压不在JLink的VTref检测范围内。先量一下目标板的3.3V引脚有没有电。
- GND没共地。JLink和目标板必须接公共地,否则电压参考点是悬空的。
- SWCLK频率太高。在Settings的Debug页面里把时钟降到1MHz以下,连接稳定之后再提高。
- 复位电路问题。有些板子拉了很长的复位线,或者复位引脚被其他器件拉低,JLink没法正确复位内核,也会报RDDI-DAP。
我的排查顺序是:先看供电,再量GND通断,再降SWCLK频率,最后查看复位引脚。四个步骤下来,大部分RDDI-DAP都能解决。真要是还不行,就把JLink拔掉重新插一次,有时候只是USB识别卡住了。
4. 添加自定义设备:从pack到FLM完成烧录闭环
4.1 用Pack Installer导入第三方芯片支持包
如果你的芯片虽然非主流,但厂家提供了Keil支持包,那是最省事的路径。现在很多国产ARM芯片厂商官网都会放CMSIS-Pack格式的支持包,下载下来是一个.pack文件,比如GigaDevice.GD32F10x_DFP.2.0.0.pack这种命名。
打开Keil的Pack Installer,在菜单栏选File -> Import,找到你下载的.pack文件双击导入。等待几秒,安装完成后,左边Device列表里就会出现对应厂商和芯片型号。之后新建工程或者打开老工程,都能直接选到这颗芯片,编译、烧录参数也会自动匹配。
这里有一个版本兼容性的坑:Pack包的格式版本和Keil主程序版本有关。新版本的pack通常要求Keil 5.30以上,老版本Keil导入新pack时可能会提示“Pack requires newer MDK version”之类的错误。解决办法要么升级Keil主程序,要么去找和当前Keil版本匹配的旧版pack。Keil 5.17这种特别老的主程序,能支持的pack版本就更有限了,所以我个人建议:还在用5.1x的同学,如果经常要接第三方芯片,至少升到5.27以上,兼容性会好很多。
4.2 完全自定义设备时PDSC文件怎么改
如果芯片厂商没有提供Pack,或者芯片是你自己做的,那就要手工建一个设备定义。这个听起来很复杂,但实际拆开来看,核心就是三板斧:一个PDSC描述文件、一个FLM烧录算法、一个SVD调试描述文件。
最简单的方式是找一份相近设备的pack,把PDSC文件解压出来后,用文本编辑器打开。PDSC文件本质是一个XML,结构大概长这样:
<package schemaVersion="1.4" vendor="MyVendor" name="MyCustom_DFP" version="1.0.0"> <devices> <family Dfamily="MyCM0" Dcore="Cortex-M0" Dvendor="MyVendor:2"> <device Dname="MyCM0F"> <processor Dcore="Cortex-M0" Pname="CM0" /> <memory id="IROM1" start="0x08000000" size="0x00010000" default="1" /> <memory id="IRAM1" start="0x20000000" size="0x00002000" default="1" /> <algorithm name="MyFlash32K.FLM" start="0x08000000" size="0x00010000" /> </device> </family> </devices> </package>关键信息就几个:芯片内核、Flash起始地址和大小、RAM起始地址和大小、以及烧录算法文件的引用。改完之后重新打包成.pack,再用Pack Installer导入,设备列表里就能看到了。
实际做的时候,我建议先在Pack Installer里把某个相近厂商的官方包导入,然后找到它的PDSC文件做参考,把厂商名、设备名、Flash大小、算法文件这些字段改成你自己芯片的。第一次不追求完美,只要Keil能识别、能编译、能烧录,就算成功。SVD文件如果没有,也不影响烧录,只是调试时看不到外设寄存器的具体名字,暂时可以先不折腾。
如果连pack导入这条路都嫌麻烦,还有一个临时的土办法:新建工程时不选具体芯片,选一个相近的通用设备,比如“Generic Cortex-M3”,然后编译后在Options for Target -> Utilities里手动勾选“Use Flash Programming Algorithm”,从算法列表里选一颗相近的FLM。这个方法不需要做pack,但工程参数和实际设备可能不完全匹配,只适合应急验证程序逻辑,不建议作为正式开发流程。
4.3 必须搞懂的FLM烧录算法
FLM文件是Keil用来写Flash的“驱动插件”。Keil下载程序时并不是直接往内存地址写,而是调用FLM里的擦除、编程、校验函数来操作Flash。所以没有对应的FLM,哪怕你在Device列表里选到了芯片,下载时也会报Cannot Load Flash Programming Algorithm。
FLM文件放在Keil安装目录的ARM\Flash下。打开这个目录,你会看到一大堆厂商前缀的.FLM文件。你的自定义设备如果没有自己的FLM,可以试着从里面找一颗“内核相同、Flash规格接近”的FLM来替代。比如你的自制MCU是Cortex-M0内核、Flash 32KB,那找一个同样Cortex-M0、32KB的FLM,大概率能用。但注意,FLM里包含的是具体Flash控制器时序,不同厂商的Flash控制器差异可能很大,替代失败也正常。只要下载时提示“Erase Done. Programming Done. Verify OK.”,就说明算法是对的。
更好的方案是让厂家提供官方FLM,或者基于CMSIS-Pack的Flash算法模板自己写一个。这个工程量不大,但需要目标芯片Flash控制器的寄存器手册,新手可以先从替代方案入手,跑通流程再考虑定制。
4.4 把外部Flash算法也加进Keil
很多时候内部Flash不够用,需要把字库、配置文件或者部分代码放进外部Nor Flash。这种情况下,只有内部Flash算法还不够,得把外部Flash的算法也加进来。
操作分两步。第一步,找到对应Nor Flash芯片的FLM文件。很多MCU厂商在自家的Pack包里会附带外部Flash烧录算法,比如STM32系列就有对应的SPI Nor Flash算法。把它复制到Keil的ARM\Flash目录。第二步,在Options for Target -> Debug -> Settings -> Flash Download页面里点Add,从算法列表里选外部Flash算法,并填写起始地址和大小。填写的起始地址要和你的外部Flash映射地址一致,这个地址在芯片手册里查得到。
如果实在找不到现成的外部Flash FLM,比如Nor Flash型号太老旧,那可以绕开Keil,直接用JLink命令行工具来烧。在命令行里执行JLink.exe进入交互模式,然后依次输入设备型号、连接方式、加载地址和文件:
loadbin C:\app.bin 0x90000000 savebin C:\backup.bin 0x90000000 0x10000loadbin把二进制文件加载到外部Flash地址,savebin可以把Flash内容读回来做校验。这个方式虽然不在Keil的下载流程里,功能却一点不少,特别适合验证外部Flash映射地址是否正确。
5. 常见问题与排查技巧实录
5.1 常见报错排查速查表
这一节把我在实际项目中遇到过的报错整理成表格,方便大家对照排查:
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
| No J-Link found | USB线没接好、驱动没装、DLL替换错误 | 检查设备管理器、换USB线、确认用的是x86目录DLL |
| The connected J-Link is defective | DLL版本与固件不匹配、兼容设备固件故障 | 替换Keil内置DLL、换老版本驱动、不要乱升固件 |
| RDDI-DAP Error | 目标板供电异常、GND没共地、SWCLK频率过高 | 先量VTref,再共地,再降SWD频率 |
| Cannot Load Flash Programming Algorithm | 缺少FLM算法或算法与芯片不符 | 加Flash算法,或用同内核FLM替代 |
| Flash Download failed - "Cortex-M4" | 内核选错、Flash算法起始地址不对 | 核对Device列表里的内核、检查Flash Download算法起始地址 |
| SWD/JTAG Communication Failure | 目标芯片进入了低功耗模式或SWD引脚被复用 | 按住复位键连接,或通过Boot引脚启动再擦除 |
这些报错看起来很吓人,但大多数情况下不是硬件坏了,而是配置链路里某一环没对上。按表格顺序检查一遍,能解决八成问题。
5.2 调试中的三个细节坑
第一个是看门狗没关。程序里如果开了独立看门狗IWDG,进入调试后单步执行时间稍长,看门狗超时就会不断复位内核,体现为“总是跳到0x0000或者复位向量”。解决方式是在Debug设置里配置Initialization File,也就是.ini脚本,在连接后把看门狗关掉。脚本内容大致就是写寄存器键值和关看门狗寄存器,具体地址要看芯片手册,但思路是通用的。
第二个是低功耗模式导致SWD连不上。主程序运行后芯片进入Sleep或Stop模式,内核时钟停了,JLink抓不到目标芯片。这时候最粗暴有效的方法是:按住目标板的复位键不放,点Keil的下载,在下载开始瞬间松开复位键。原理是芯片上电复位阶段内核还在运行,JLink抓住这个窗口建立连接。如果手法不熟练,多试几次就找到感觉了。
第三个是DLL替换后Keil识别正常,但一更新Segger驱动又废了。我后来养成了一个习惯:每次替换DLL之前,把Keil的ARM\SEGGER整个目录压缩备份。因为新驱动安装时往往会重新覆盖或增加一些文件,搞得我之前调好的组合又乱了。备份好之后,随时能一键还原。
5.3 为什么这个经验不一定通用
题目里我说“经验自留,不一定通用”,这是真心话。因为Keil主程序版本、Segger驱动版本、JLink固件版本、目标芯片类型这四个变量排列组合起来,可能性太多了。我在A环境里调好的DLL组合,放到B环境里可能就不好使。所以这篇文章真正有价值的部分,不是告诉你“某个版本一定行”,而是给你一套排查思路:先确认硬件OK,再锁定DLL和固件版本,最后检查Flash算法和配置项。按这个顺序走,基本都能找到问题点。
另外提醒一点,替换任何文件前都做好备份。不要小看这个习惯,我见过有人把JLinkARM.dll换成新版本后,Keil连老工程都打不开了,最后只能重装整个Keil。有备份的话,十秒钟就能还原回来。
6. 写在最后的一点个人习惯
这些年在多个项目里折腾JLink和自定义设备,我最大的体会是:工具链没有“一劳永逸”的配置,只有“当前项目可用”的配置。每次拿到一块新板子或者新芯片,我都会先花五分钟把三样东西记下来:Keil主程序版本、Segger驱动版本、目标芯片的Flash算法来源。有了这组记录,下次再遇到报错,对照起来特快。
如果你现在正好被Keil的JLink识别问题卡住,可以先从替换ARM\SEGGER目录下的DLL开始试,再按表格排查接线和算法。要是这篇文章能帮你少走一次弯路,那这趟记录就值了。