板子打样回来,第一次上电,探针插上去,软件里点"连接",转圈三秒,弹出一行红字——这种场面做过嵌入式的人应该都不陌生。多数时候问题不在代码,而在于你面前那几根线到底走的是 JTAG 还是 SWD,以及它们背后那套状态机有没有被你真正读懂。JTAG 和 SWD 这两个词几乎每个做 MCU 开发的人都挂在嘴边,但能把它们的时序、包结构、引脚约束、故障表现讲清楚的人并不多,很多人只是记住了"SWD 只要两根线"这一句话,然后在真正遇到连不上、掉线、烧一次就锁死的时候束手无策。这篇内容围绕 JTAG 与 SWD 两个调试接口展开,从协议原理、状态机、包结构,到硬件设计约束、配置文件写法、故障排查实录,全部拆开讲透,适合刚接触 MCU 调试的新手建立完整认知,也适合已经做过几个项目、但每次遇到调试口问题都靠试错解决的老手补齐底层逻辑。文中涉及的芯片型号、探针品牌、工具名称一律用通用代称,不影响任何一条结论的复现。
1. 先把两个接口摆到桌面上:它们到底在解决什么问题
1.1 调试接口的本质是一条通往芯片内部的窄门
很多人把调试接口理解成"下载程序的通道",这个理解只对了一小半。下载固件只是它最表层的能力,真正值钱的是在线访问内核寄存器和总线:读 PC、改内存、下断点、单步、看变量、抓 ITM 打印,这些动作全都建立在一条能对芯片内部地址空间发起读写请求的物理链路上。这条链路带宽很低、线很少,但它是芯片设计时专门留出来的后门,一旦固件把它锁上,你就只能靠擦除整片 Flash 来重新进门。
理解这一点之后,JTAG 和 SWD 的差异就好解释了:它们不是两种"下载方式",而是两种访问调试访问端口(DAP)的传输层。芯片内部真正干活的是 DAP,它下面挂着 AHB-AP 或 APB-AP,AP 再把请求翻译成总线事务打到内存和外设上。JTAG 和 SWD 的区别,本质上只是"用什么方式把请求包送进 DAP"——一个用四条线跑状态机扫链,一个用两条线跑半双工串行包。
这个分层认知非常关键。它意味着:当你遇到"能识别芯片但读不了内存"的问题时,故障点大概率在 AP 或总线上,而不是 JTAG/SWD 的物理层;反过来,当你遇到"完全扫不到 IDCODE"时,问题一定在最底下的物理链路。把层次分清,排查才不会乱。
1.2 两者出身不同,决定了各自的能力边界
JTAG 出身于板级互连测试。上世纪八十年代多层板普及,BGA 封装让探针根本够不到引脚,于是有了边界扫描这套思路:在每个 IO 引脚上塞一个移位寄存器单元,串成一条链,测试时把激励从 TDI 打进去,从 TDO 读出来,就能判断焊点有没有虚焊、走线有没有短路。这套东西后来标准化成了 IEEE 1149.1,芯片厂商顺手把它复用成调试通道——反正链已经在了,多定义几条指令就能读内核。
SWD 则是为调试而生的。它由内核 IP 提供方定义,从第一天起目标就只有一个:用最少的引脚、最紧凑的包结构,把 DAP 访问做得又快又省线。所以它没有边界扫描能力,没有菊花链,不支持多器件串接,就是一条点对点的两线链路。
这个出身差异带来一个实际后果:JTAG 能做板级测试,SWD 不能。如果你在做小批量试产,需要检查 BGA 下面有没有虚焊,那 JTAG 的边界扫描是唯一不需要 X 光的手段。反过来,如果你的产品是个指甲盖大小的模组,PCB 面积按平方毫米算钱,那多出来的两根线就是纯成本,SWD 才是正解。
1.3 引脚数量与资源开销的账要算清楚
先把账摆出来,后面所有讨论都建立在这张表上。
| 维度 | JTAG | SWD |
|---|---|---|
| 必需信号线 | TCK、TMS、TDI、TDO(4 根) | SWCLK、SWDIO(2 根) |
| 可选信号线 | TRST、VTREF、nRST | SWO、VTREF、nRST |
| 典型连接器 | 20 pin 2.54mm、10 pin 1.27mm | 10 pin 1.27mm、4 pin 2.54mm 简易排针 |
| 是否支持多器件 | 支持,菊花链串接 | 不支持,点对点(多点模式实际很少落地) |
| 是否支持边界扫描 | 支持 | 不支持 |
| 传输方向 | 全双工(TDI 进、TDO 出同时) | 半双工(SWDIO 双向分时复用) |
| 典型时钟 | 几 MHz 到几十 MHz | 几 MHz 到几十 MHz,实际常用 4~10MHz |
| 引脚复用风险 | 较低(多数芯片默认保留) | 较高(常与 GPIO 复用,固件容易占掉) |
注意最后一行。这是我在实际项目里踩过最多次的坑:SWD 只占两根线,芯片厂商为了省引脚,几乎一定把 SWDIO/SWCLK 和普通 GPIO 复用在一起。固件初始化时如果顺手把这两个引脚配成了推挽输出,探针下次就连不上了。JTAG 的四根线因为数量多,反而往往被单独留出来,复用概率低一些。
1.4 先给一个可以直接抄的选型结论
不绕圈子,日常项目我基本按这几条走:
- 新项目一律优先 SWD。省引脚、省连接器、速度快、接线不容易错,尤其是 4 pin 简易排针,产线工人闭着眼睛都能插对。
- 需要板级边界扫描测试、或者目标芯片只支持 JTAG时才用 JTAG。
- 多芯片串在一条链上(比如一个主控加一颗 FPGA 或者一颗 CPLD),JTAG 菊花链更省探针。
- 调试早期保留 nRST。这条线在救砖场景里价值极高,省掉它省不出多少成本,但会让你在锁死时多花半天。
- SWO 一定要引出来。很多项目把 SWO 省了,结果后面想看 ITM 打印只能靠串口,白白浪费一个引脚。
2. JTAG 拆解:从 TAP 状态机到菊花链
2.1 四根线背后的十六状态机
JTAG 的核心是 TAP 控制器(Test Access Port),它本质上是一个 16 个状态的有限状态机,由 TCK 和 TMS 两根线驱动。TMS 在 TCK 的上升沿被采样,采样值决定下一步跳到哪个状态;TDI 同样在上升沿采样,TDO 则在下降沿更新。这个相位关系是硬件设计的铁律,如果你的探针和目标之间因为线太长导致 TDO 到达时已经错过采样窗口,读回来的数据就会全错,表现出来就是"能连上但读出来的 ID 是乱的"。
16 个状态里有几个是必须记住的:
- Test-Logic-Reset:TMS 连续拉高五个 TCK 就能进去,这是所有状态的复位入口,任何异常都可以靠这一招回到起点。
- Run-Test/Idle:状态机的"休息室",指令执行和数据寄存器更新都在这里完成,停顿的时候应该停在这里。
- Shift-DR / Shift-IR:唯一两个真正移位数据的状场。TDI 进、TDO 出,一个 TCK 一位。
- Capture-DR / Capture-IR:把当前并行数据装进移位寄存器,是"拍照"的动作。
- Update-DR / Update-IR:把移位寄存器里的值并行锁存到目标,是"生效"的动作。
我见过不少人在写自定义调试脚本时卡住,问题往往出在忘了走 Update 这一步:数据已经移进去了,但没有 Update,芯片那边根本没看见新值。判断方法很简单,用示波器抓 TMS 波形,看有没有走完 Shift → Exit1 → Update 这条路径。
2.2 IR 与 DR:指令寄存器和数据寄存器的配合
JTAG 的访问模型是"先选指令,再传数据"。每次要访问某个寄存器,先把指令扫进 IR,然后通过 DR 扫链读或写对应的数据寄存器。
指令寄存器的长度由芯片定义,常见是 4 位或 5 位。这个长度在扫链时非常关键:菊花链上所有器件的 IR 是串联的,你要往第 N 个器件写指令,必须把前面所有器件的 IR 位都填上(填 BYPASS 指令即可),否则指令会错位到别的器件上。
几条标准指令必须知道:
- BYPASS(全 1):把该器件的 DR 缩成 1 位,作用是"透传",让链路能绕过这个器件直通下一个。
- IDCODE(由芯片定义):读 32 位器件 ID,包含厂商码、部件号、版本号。
- EXTEST(全 0):边界扫描测试用,把边界扫描单元接到引脚上。
- SAMPLE/PRELOAD:采样引脚当前电平,不影响正常工作。
BYPASS 那个 1 位的设计非常巧妙,它不是随便定的:DR 链上每个器件至少要有 1 位,否则链就断了。所以哪怕是"绕过"这个动作,也需要占 1 位位置来维持信号的通路。
2.3 菊花链:长度算错一位就全盘皆输
菊花链是 JTAG 相对 SWD 最大的结构性优势,也是最容易算错的地方。串联时,前一级的 TDO 接后一级的 TDI,TCK 和 TMS 并联给所有器件。这样一条链上所有器件的 IR 和 DR 都变成了串联的移位寄存器。
关键结论是:总 IR 长度 = 所有器件 IR 长度之和;总 DR 长度 = 所有器件当前 DR 长度之和。而 DR 长度是动态的,取决于你给每个器件下的指令。
举个具体例子。假设链上有三个器件,IR 长度分别是 4、5、4,那么总 IR 长度是 13 位。你要给中间那个器件下 IDCODE 指令,就得构造一个 13 位的数据:前 4 位填第一个器件的 BYPASS 码,中间 5 位填 IDCODE 码,最后 4 位填第三个器件的 BYPASS 码。
接下来读数据。第一个器件在 BYPASS 下 DR 是 1 位,第二个器件在读 IDCODE 时 DR 是 32 位,第三个器件 BYPASS 也是 1 位,总 DR = 34 位。移出的 34 位里,中间 32 位才是你要的 IDCODE,前后各 1 位是旁路填充位。很多人在这一步直接把前 32 位当结果,读出来就是错的,因为位序和你想象的相反——链上第一个器件的 TDO 最后移出,实际读到的顺序是从链尾到链首。
这个细节我在第一次做多器件链的时候卡了整整一个下午。解决办法其实很朴素:先只扫 IDCODE 不看结果,把整条 34 位打印出来,手工数一下哪 32 位看起来像合法的 IDCODE(合法 IDCODE 的最低位固定是 1),确认位序之后再写解析代码。
2.4 JTAG 的优势区间和它的代价
把优势摆清楚:全双工、有独立回读线所以抗干扰相对好、支持菊花链、支持边界扫描、协议标准化程度极高,几乎所有带调试口的芯片都留了这条路。TRST 这根可选线还能独立复位 TAP 状态机,不用影响整个芯片。
代价也很明显。四根线加地线,连接器尺寸下不来;每访问一个寄存器都要先扫一次 IR,指令开销大;TCK 是全部器件共享的,链越长,时钟能跑的上限越低,因为每个器件的建立保持时间都要满足;边界扫描虽强,但需要厂商提供 BSDL 文件才能用,很多小厂根本不给。
还有一个常被忽略的代价:JTAG 引脚被固件占用的场景很隐蔽。有些芯片的 JTAG 引脚在上电后默认就是 JTAG 功能,但如果固件里调用了某个外设初始化,把那几个引脚重新映射成 GPIO,探针就会在程序跑起来之后突然掉线——连接时是好的,一运行就断。这种时序性的掉线比一开始就连不上更折磨人,排查时要在复位后立刻暂停内核(连接时勾选"复位即暂停"),抢在固件重映射引脚之前把调试链路握住。
顺带提一句 JTAG 与 SWD 之间是可以切换的。内核在 TAP 状态机上留了一个特殊序列:在 TMS 上按 LSB 优先发 16 位0xE79E,那就是 SWD 的切换码,之后跟至少 50 个 TCK 的高电平,TAP 就会切到 SWD 模式。反过来从 SWD 回 JTAG 也有对应的切换序列。如果你的探针同时支持两种模式,软件里切换接口类型其实就是在发这个序列。
3. SWD 拆解:两根线怎么做到 JTAG 的功能
3.1 物理层:半双工双向线与那一个 turnaround 周期
SWD 只有两根线:SWCLK 由探针驱动,SWDIO 双向分时复用。因为是双向的,就必须有方向切换的时刻,这个时刻叫做 turnaround,标准里规定是 1 个时钟周期的间隙。也就是说主机发完请求包之后,要空一个时钟让线翻转方向,然后目标才能驱动 SWDIO 返回应答。
这一个周期的间隙看起来微不足道,但它是很多时序问题的根源。当你的排线过长、或者线上串了不合适的分压电阻、或者 SWDIO 的上拉阻值太大,这个翻转沿就会变缓,目标是靠这个沿来采样和驱动的,一旦沿不够陡,通信就开始随机失败——表现是"有时能连有时不能,换根短线就好了"。我一般建议在 SWDIO 和 SWCLK 上各串一个 22Ω 到 100Ω 的电阻,靠近探针一侧放,抑制振铃;SWDIO 加上拉(10kΩ 典型),SWCLK 加下拉。很多芯片内部已经集成了这些电阻,画板前一定要翻数据手册的调试口章节确认,重复加上拉会让电平被拉死。
3.2 包结构:8 位请求、3 位应答、32 位数据
SWD 的一次完整传输由四段组成,每一段都要精确理解:
请求包(8 位,主机驱动):起始位固定为 1,然后是 APnDP 位(选 DP 还是 AP)、RnW 位(读还是写)、两位地址 A[2:3]、1 位校验位(对这几位做奇偶校验)、停止位固定 0、停放位固定 1。这个结构比 JTAG 的扫链紧凑太多,一次只花 8 个时钟。
turnaround(1 个时钟):方向切换。
应答包(3 位,目标驱动):0b001表示 OK,0b010表示 WAIT(目标还没准备好,可以重试),0b100表示 FAULT(出错了,需要读 CTRL/STAT 寄存器清错误标志)。WAIT 应答是正常现象,不是故障,调试器通常会重试几次。如果你自己写底层驱动,一定要实现 WAIT 重试逻辑,否则在大批量读写时会莫名其妙失败。
数据段(33 位):32 位数据加 1 位奇偶校验,读操作时前面还要再加 1 个 turnaround。
把这几段加起来,一次读传输大约 46 个时钟,一次写传输大约 46 个时钟(读多一个 turnaround)。按 10MHz 算,理论上每秒能跑二十多万次传输,实际因为软件开销会低不少,但比 JTAG 每次都要扫 IR 的效率高出一个量级。
3.3 DP 与 AP:两层寄存器模型
SWD 访问的不是扁平地址空间,而是两层结构:DP(Debug Port)和 AP(Access Port)。这一点常常让刚接触的人困惑——为什么读内存要先写一串寄存器?
DP 是调试端口本身,管的是链路的控制。常用寄存器只有四个:DPIDR(读 ID,验证链路是否通)、CTRL/STAT(控制与状态,包含电源请求位和错误标志)、SELECT(选择当前操作的 AP 和寄存器组)、RDBUFF(读缓冲)。
AP 才是通向芯片总线的那一端。AHB-AP 上最常用的三个寄存器:CSW(控制与大小,配置传输宽度和地址自增)、TAR(目标地址)、DRW(数据读写)。要读内存某个地址,流程是:写TAR填地址,然后读DRW拿数据;连续读的时候在CSW里打开地址自增,硬件会自动帮你把 TAR 加上 4。
这里有一个只有实际调试才会遇到的细节:读DRW拿到的是上一次传输的结果。因为 AP 访问是流水线式的,你在读DRW的那个周期里,目标才刚开始去总线上取你要的地址的数据,取回来的值存在RDBUFF里,要再读一次RDBUFF才能拿到。用现成的调试软件你不需要关心这个,但如果你自己在移植底层驱动或者写量产工具,这是最容易出错的点之一。
AP 的选择是通过SELECT寄存器的APSEL字段(高 8 位)和APBANKSEL字段(第 4 到 7 位)完成的。多核芯片上常常挂多个 AP,每个核一个,选错了 AP 就会读到别的核的地址空间。
3.4 复位序列与 SWD 的能力边界
每次连接之前,调试器要发一个线复位序列:先把 SWDIO 拉高输出至少 50 个时钟,这会让目标识别到同步头,然后发 16 位的0xE79E(LSB 优先),目标据此确认切到 SWD 模式。这个序列的意义在于抗同步丢失:如果目标跑飞了、或者时钟被打乱,重新发一遍就能重建链路。
SWD 的能力边界也很清楚:
- 不支持边界扫描,做不了板级互连测试。
- 不支持菊花链。理论上后来引入过多点模式(通过 SWDIO 上拉/下拉电阻区分目标地址),但支持的芯片极少,实际项目基本碰不到,多目标要么用多路开关板,要么就多插几次探针。
- 引脚复用风险高,前面说过,这是 SWD 最现实的软肋。
- 兼容性依赖内核实现,不同厂商的 DAP 实现细节有差异,比如电源域控制位、复位行为,换芯片时配置要重新调。
至于速度,SWCLK 的标称上限能到很高,但真正能跑稳多少取决于走线、排线质量、目标芯片的同步逻辑。我的经验是:桌面上用 10cm 以内的短线,跑 10MHz 基本没问题;用 20cm 的排线接到工装治具上,4MHz 更保险;产线环境有电机和干扰源,1MHz 反而最稳。速度不是越高越好,能跑通并且不掉线才是好速度。
4. 硬件设计阶段的取舍:引脚、上下拉与 PCB
4.1 引脚复用与上电默认状态
设计阶段最该盯的是数据手册里的"引脚复用表"和"调试口保持条件"两节。多数 MCU 在复位后默认把调试引脚配成调试功能,但这个"默认"往往有前提:某些芯片要求在选项字节里勾选调试使能,某些芯片在进入低功耗模式后会自动释放调试引脚,还有些芯片在启动流程走完、用户代码初始化外设后,调试引脚就归外设管了。
我在一个低功耗项目上吃过亏:设备正常进休眠之后,探针就再也连不上,必须手动断电再上电、抢在休眠前几十毫秒内连接。后来解决办法是在配置里打开"复位时连接"(connect under reset),让调试器先拉住 nRST 不放,等链路建立后再释放,芯片从复位向量开始跑,调试链路已经建立好了。这个策略几乎能救回八成"能烧一次但之后连不上"的板子。
注意:把 nRST 接到调试连接器上不是可选项。很多省成本的板子只引 SWCLK、SWDIO、GND、VTREF 四根线,省掉的正是救命的那根。
4.2 上下拉、串联电阻与电压域
上下拉这件事,我的原则是"以芯片手册为准,不重复加"。典型配置是这样:
| 信号 | 默认偏置 | 串联电阻 | 说明 |
|---|---|---|---|
| SWCLK | 下拉 | 22~100Ω | 时钟线悬空时保持低电平,避免误触发 |
| SWDIO | 上拉 | 22~100Ω | 空闲时保持高电平,配合线复位序列 |
| nRST | 上拉 | 100Ω | 上拉保证正常运行时不被误复位 |
| TCK | 下拉 | 22~100Ω | 同 SWCLK |
| TMS | 上拉 | 22~100Ω | 保证 TAP 不进复位 |
| TDI | 上拉 | 22~100Ω | 避免悬空时扫入随机数据 |
| TDO | 无 | 22~100Ω | 目标输出,只在探针侧串阻 |
串联电阻的作用是抑制振铃和过冲,不是"限流"。放在靠近连接器或者靠近探针的一端效果最好。如果你的探针和目标之间超过 15cm,建议加上;短线加不加影响不大。
电压这块有个常见错误:VTREF 必须接。调试器靠这根线检测目标电压,据此调整 IO 电平。VTREF 没接或者接错,表现是"能识别探针但读不到目标"。另外,如果目标板是 1.8V 逻辑而探针默认 3.3V,那也要确认探针支持宽压,否则会一直通信失败。
4.3 连接器选型与走线约束
连接器选型没有绝对的标准,看场景:
- 开发板、实验板:20 pin 2.54mm 是通用选择,兼容性好,缺点是占地方。
- 小体积产品:10 pin 1.27mm 是主流,SWD 加 SWO 加复位加电源正好够。
- 成本敏感的量产板:4 pin 或 5 pin 的 2.54mm 排针最实在,只留 SWCLK、SWDIO、GND、VTREF,加上复位就是 5 pin。产线治具做成弹簧针,插拔效率高。
走线方面有几条硬约束值得写进设计规范:
- SWCLK 和 SWDIO 尽量等长、靠近走,中间不要夹高速信号线。它们是低速信号,但旁边如果有开关电源或者高速差分对,耦合进来的噪声会让 turnaround 时序出错。
- GND 至少给两根。地弹是低速链路失效的隐形杀手,尤其是探针和目标板地电位不一致、共模干扰大的场景。
- 走线总长控制在 30cm 以内,超过这个长度就要降速,或者考虑加缓冲。
- 不要和调试口共用那些在复位时会跳变的电源轨。有些板子把调试口的 VTREF 接到一个可控 LDO 输出上,上电时序一变,探针检测到的电压就不稳定。
4.4 SWO 那根最容易被浪费的引脚
SWO(也叫 TRACESWO)在 Cortex-M 上是一个单向输出引脚,用来做 ITM 打印和指令跟踪。它的价值在于:不占串口、不需要额外引脚、带宽比串口高得多,而且是在线实时输出,程序跑多快它就能输出多快(受限于时钟比例)。
配置上,SWO 的波特率是 SWCLK 的一个分频比,常见比值是 1/2 到 1/32。用 10MHz 的 SWCLK 配 1/4 分频,SWO 就是 2.5Mbaud,打印几十行日志几乎不占时间。相比之下,串口 115200 打印一行几十字节要花几毫秒,对有时序要求的场景干扰很大。
但 SWO 有两个前提容易翻车:一是引脚必须引出来并且接到探针的对应引脚上(很多 4 pin 简易排针没有这一路);二是目标固件必须初始化 ITM 和 TPIU,很多人以为打开调试软件里的选项就行,其实固件里那几行初始化代码不写,输出永远是空的。我在一个项目上排查了半小时,最后发现是忘了在启动代码里打开 TRCENA 位。
5. 实战配置:把链路跑通并稳定提速
5.1 连接拓扑和最小可跑通集合
先给一个最小可跑通的接线,以 SWD 为例,五根线:
| 探针侧 | 目标侧 | 备注 |
|---|---|---|
| VTREF | 目标 VCC(逻辑电平) | 必接,用于电平检测 |
| GND | GND | 至少两根,多多益善 |
| SWCLK | SWCLK | 串 22Ω 更稳 |
| SWDIO | SWDIO | 上拉 10kΩ |
| nRST | nRST | 救砖关键,强烈建议 |
接好之后先不上电,用万用表量一下 VTREF 对地是不是短路,SWCLK 和 SWDIO 对地有没有异常阻抗。这一步看着多余,但能挡掉相当一部分"接错线"的低级错误——我见过把 VTREF 和 GND 插反的,探针的保护电路直接烧掉。
上电之后第一件事不是点连接,而是先确认目标芯片是不是在跑:量一下电源轨、量一下复位脚是不是高电平。如果复位脚是低电平或者一直抖,说明芯片在反复复位,这时候点连接大概率失败,先解决复位问题。
5.2 配置文件的关键字段
不同调试软件的配置文件语法不一样,但字段含义是相通的。下面给一份通用示意,字段名按常见开源调试服务器的习惯写,具体到你的环境要对照手册:
# 接口类型:二选一 transport select swd # 或 transport select jtag # 适配器时钟,单位 kHz,先从低速起 adapter speed 1000 # 目标电压,部分适配器需要显式指定 adapter voltage 3300 # 复位行为:none / srst_pulls_trst / trst_only / srst_only reset_config srst_only srst_nogate # 目标芯片描述 source [find target/xxx.cfg]几个字段值得单独解释:
adapter speed是最该先动的一个。我的习惯是初始化阶段一律先降到 1000kHz 甚至 500kHz,确认整条链路(IDCODE、DPIDR、内存读写)全部正常,再逐步往上加,每加一档都重新验一遍。这样一旦出问题,你能立刻知道是哪一档开始不稳。
reset_config是排查复位相关问题的核心。srst_only表示只用系统复位脚;srst_nogate表示复位期间不阻塞调试访问。如果你发现"连接时一切正常,一点运行就断",多半是复位策略和固件的引脚重映射打架,试试换成connect under reset的流程。
5.3 分阶段验证链路
不要上来就读内存。按这个顺序一步步来,每一步都有明确的成功标志:
第一步,验证探针本身。不接目标,只上电探针,看软件里能不能识别到探针型号和固件版本。识别不到说明驱动或者 USB 连接有问题,跟目标板一点关系都没有。
第二步,验证物理链路。接上目标,读DPIDR(DP 的 ID 寄存器)。这个值应该是芯片手册里给的那个固定值,读到0xFFFFFFFF说明 SWDIO 一直是高、根本没通;读到0x00000000说明线被拉低或者 SWCLK 没出来;读到一个固定的非预期值,可能是线序接反。
第三步,验证 AP 通路。通过 AP 读芯片的 ID 寄存器(多数芯片在0xE0040000附近有一块 ROM Table)。读不到说明 DP 通了但 AP 有问题,常见原因是芯片在低功耗状态,需要先唤醒。
第四步,验证内存读写。往一段 RAM 里写一个特定的图案(比如0x5A5A5A5A),再读回来比对。图案要选得刁钻一点,用递增序列能同时检出位错位和地址错位。
第五步,上真程序。烧一个最小固件,确认复位后能正常启动,再确认烧写速度符合预期。
这五步跑完,链路的健康状态就是清楚的。以后遇到问题,直接回到对应步骤定位,不用从头猜。
5.4 提速与稳定性之间的平衡
链路跑通之后,很多人会立刻把速度拉到最高,然后抱怨"不稳定"。这里有个经验法则:能稳定跑的最高速度,通常比标称上限低两到四倍。标称 20MHz 的接口,实际稳在 5~10MHz 是正常的。
提速的方法不是硬拉时钟,而是改善物理条件:缩短排线、加地线、加串联电阻、把探针的供电从 USB 换成独立电源。我在一个工装治具上做过对比,同样是 10MHz,20cm 排线换 8cm 排线之后,连续烧写一百次的失败率从 7 次降到 0 次。
还有一个提效思路是改变操作方式,而不是改时钟:批量烧写用脱机烧录器(不需要电脑参与,一次装夹,按下按钮就走完擦除、写入、校验),比在线烧写的效率高得多;调试时用 SWO 而不是串口打印;连续读内存时打开 AP 的地址自增,让硬件帮你递增地址。
6. 故障排查实录:连不上的那些具体原因
6.1 硬件层的排查顺序不能乱
链路不通,先查硬件,顺序是固定的:电源 → 复位 → 地 → 时钟 → 数据。跳步会浪费大量时间。
电源:目标是不是在正常电压范围内,VTREF 有没有接到正确的逻辑电平上,探针和目标是不是共地。共地这一条听着离谱,但我确实见过用独立电源给目标供电、忘了把地接通的场景。
复位:nRST 是不是在高电平,有没有周期性脉冲(那说明在看门狗复位)。如果 nRST 被固件配成了 GPIO 输出低电平,探针拉不低也拉不高,这时候要靠"复位时连接"策略,抢在上电初期建立链路。
地:地线阻抗是否过大,GND 引脚有没有虚焊。用万用表量探针地到目标地的电阻,应该接近 0。
时钟:SWCLK 上有没有波形,幅度对不对。用示波器抓一下,如果上升沿很缓(超过周期的 10%),就是驱动能力不够或者线上电容太大。
数据:SWDIO 空闲时是不是高电平,上拉有没有装。
6.2 几类典型的"能连上但用不了"
现象一:能识别芯片,一读内存就报总线错误。大概率是 AP 选错了,或者目标外设时钟没开。AHB-AP 去访问一个时钟关闭的外设,会返回总线错误。解决办法是先通过调试器把外设时钟使能,或者干脆避开那些区域。
现象二:能烧写,烧完就再也连不上。这是最常见的锁死场景,原因有三类:固件把 SWD 引脚重映射成 GPIO;固件进入低功耗模式后关闭了调试域时钟;固件设置了读保护但没留擦除路径。应对办法按顺序试:先试"复位时连接",再试降低时钟,还不行的用"擦除整片"(mass erase)——多数芯片支持在复位保持的状态下通过调试口触发全片擦除,这个动作会清掉选项字节,把调试口恢复成出厂状态。
现象三:一开始正常,运行几秒后掉线。看门狗复位或者异常复位在反复打断链路。可以在调试软件里勾选"复位时暂停"观察是否立刻断,如果断在某个固定位置,那基本就是固件在那一步做了引脚重映射。
现象四:同一块板,换台电脑就认不到。USB 供电不足或者驱动问题。换到有独立供电的端口,或者加一个有源 Hub。另外有些探针固件版本对某些操作系统支持不好,这个只能靠更新固件解决。
6.3 常见问题速查表
| 现象 | 最可能的原因 | 先做什么 | 再做什么 |
|---|---|---|---|
| 软件提示找不到探针 | USB 驱动/线缆/供电 | 换线换口 | 重装驱动,检查探针固件 |
| 读到 ID 全 0xFF | SWDIO 无驱动,或目标未上电 | 量 VTREF 和 GND | 检查 SWDIO 上拉和线序 |
| 读到 ID 全 0x00 | SWCLK 无输出,或 SWDIO 对地短路 | 示波器看 SWCLK | 查排线通断和焊接 |
| 读到固定但错误的值 | 线序接反,或目标电压不匹配 | 逐线核对 | 确认 VTREF 电平 |
| 能连但很快掉线 | 固件重映射引脚/低功耗/看门狗 | 开"复位时连接" | 检查固件引脚初始化 |
| 低时钟能连,高速率失败 | 走线长、上拉不足、无串阻 | 降到 1MHz 验证 | 缩短排线、加串阻、加地线 |
| 烧写校验失败 | 时钟不稳,或 Flash 编程时序被干扰 | 降速重试 | 检查供电纹波 |
| 只有 JTAG 能连,SWD 不行 | 选项字节禁用了 SWD | 用 JTAG 进系统读选项 | 重新烧写选项字节 |
| 只有 SWD 能连,JTAG 不行 | 引脚被复用,或 TRST 未接 | 检查引脚复用配置 | 用 SWD 先恢复 |
| 高温或低温下失效 | 时序余量不足 | 室温下提高时钟验证 | 优化走线和上拉,降低工作频率 |
| 插上探针程序就跑飞 | 复位策略冲突,或调试域抢占 | 改 reset_config | 避免在关键时序中断言复位 |
| 多板共用工装时偶发失败 | 地环路、共模干扰 | 加独立地线,缩短走线 | 工装加屏蔽或降低时钟 |
6.4 几条只能靠踩坑换来的经验
第一条,上电顺序会影响结果。有些板子先接探针再给目标上电,链路就建立不起来;反过来先给目标上电再接探针就正常。原因是探针在检测 VTREF 的那一刻决定了后续的电平策略。遇到这种玄学问题,固定一个上电顺序,写进作业指导书。
第二条,换探针之前先怀疑线。我见过太多"探针坏了"的结论,最后发现是排线里某根地线断了。万用表逐根量,花两分钟,能省掉一次返厂。
第三条,锁死不要慌,先想清楚芯片的擦除路径。多数 32 位 MCU 都有一个"在复位保持状态下通过调试口强制擦除"的机制,这个机制不依赖用户固件,所以只要硬件链路是通的,就一定能救回来。关键是别在慌乱中反复上电,把最后一次机会也用掉。
第四条,调试口不要和产品的功能引脚共用同一段 PCB 走线。有些设计为了省空间,让调试口和某个功能接口复用同一组焊盘,插上探针时功能信号会串进来,通信质量直接崩掉。
第五条,给调试口留测试点。哪怕最终产品不引出连接器,也要在 PCB 上留五个焊盘(SWCLK、SWDIO、GND、VTREF、nRST)。返修和产线测试的时候,这五个焊盘就是救命通道,成本几乎为零。
7. 最后聊几句我自己的使用习惯
这些年下来,我的调试口设计习惯基本固定了:新板子一律 10 pin 1.27mm,把 SWCLK、SWDIO、SWO、nRST、VTREF 全引出来,地线给两根;PCB 上额外留一组 5 pin 的 2.54mm 焊盘做备用通道,位置放在板边方便夹探针;固件里所有涉及调试引脚的初始化都加注释标记,提醒后续维护的人不要随手改成 GPIO;量产固件单独出一版,在选项字节里保留调试口使能,只关闭读保护,这样既保护了代码又保留了返修能力。
至于 JTAG,我现在只在两种情况下用:目标芯片本身只支持 JTAG,或者项目需要做边界扫描测试。后一种情况其实很有价值,小批量试产的时候用边界扫描查一遍 BGA 焊点,比等组装完再发现问题省事得多,只是需要提前跟芯片厂商要 BSDL 文件,这个文件不是每个厂商都痛快给。
真要说最容易被忽略的一点,就是调试口的可靠性是设计出来的,不是调出来的。引脚留够、上下拉配对、地线给足、复位线别省,这四件事在画原理图的时候做好,后面能省掉八成以上的调试时间。等到板子回来发现连不上再去改,成本就是重新打样和一周工期。
<||DSML|| parameter name="budget_tokens">0