3. JTAG/SWD 调试接口:用 SWO 引脚撬开 CPU 的嘴
烧录失败后,芯片往往还活着,只是不按你的意愿跑。这时候最怕什么?怕它「沉默」。JTAG/SWD 接口就是撬开它嘴的那把螺丝刀,而 SWO 引脚,是它愿意开口说话的那条缝。
我早年做一款电机驱动板,烧录到一半掉电,重新上电后芯片死活连不上调试器。当时急得满头汗,后来发现其实是 SWD 的 SWCLK 引脚被一个 10k 下拉电阻拉死了。嗯,从那以后,我每次画板子都会专门检查调试接口的上下拉配置。
3.1 JTAG 与 SWD:选哪个?
先搞清楚这两个接口的区别。JTAG 是大哥,5 根线,功能全;SWD 是小弟,2 根线,够用还省引脚。现在绝大多数 Cortex-M 内核的芯片都支持 SWD,我个人习惯——只要不是必须用 JTAG 做边界扫描,一律用 SWD。
| 特性 | JTAG | SWD |
|---|---|---|
| 引脚数 | 5(TCK, TMS, TDI, TDO, nTRST) | 2(SWCLK, SWDIO) |
| 调试功能 | 完整(含边界扫描) | 完整(不含边界扫描) |
| 烧录速度 | 最高 50 MHz | 最高 50 MHz(实际受线长影响) |
| 适用场景 | FPGA、复杂 SoC、多核调试 | 绝大多数 Cortex-M 单片机 |
| SWO 支持 | 需额外引脚 | 原生支持(SWO 独立引脚) |
3.2 SWO 是什么?为什么它这么重要?
SWO(Serial Wire Output)是 SWD 协议里的一条单向输出线。它不参与烧录,也不参与调试握手,它的唯一任务——把 CPU 内部的状态信息「吐」出来。
说白了,它就是 CPU 的「黑匣子数据链」。烧录失败后,芯片可能跑飞、死循环、或者卡在某个异常向量里。你没法打断它,也没法单步调试。但 SWO 可以让你在不打断 CPU 的情况下,实时看到 PC 指针、异常号、中断嵌套层级、甚至 printf 输出。
为什么会这样?因为 SWO 走的是 ITM(Instrumentation Trace Macrocell)模块,它直接挂在 CPU 内核总线上,不占用用户代码的执行时间。你想想看,这相当于给 CPU 装了一个独立的「日志记录仪」。
3.3 硬件连接:别让一根线毁了所有
连接其实很简单。以 ST-Link/V2 为例:
- SWCLK → 芯片 SWCLK 引脚
- SWDIO → 芯片 SWDIO 引脚
- SWO → 芯片 SWO 引脚(也叫 TDO 或 TRACESWO)
- GND → 共地
- 3.3V → 目标板供电(可选,建议独立供电)
3.4 软件配置:让 SWO 真正工作起来
硬件接好了,软件不配等于白搭。以 STM32 为例,你需要做两件事:
- 使能 ITM 和 SWO 时钟——在 SystemInit() 或你的初始化代码里配置。
- 设置 SWO 波特率——通常与调试器协商,但也可以手动指定。
下面是我常用的初始化代码片段:
/* 使能 ITM 和 SWO */
void SWO_Init(uint32_t portBits, uint32_t cpuFreqMHz, uint32_t swoFreqMHz)
{
uint32_t demcr = *(volatile uint32_t *)0xE000EDFC;
demcr |= (1 << 24); // 使能 ITM 全局
*(volatile uint32_t *)0xE000EDFC = demcr;
/* 设置 SWO 波特率:SWO 频率 = CPU 频率 / (SWO 分频 + 1) */
uint32_t prescaler = (cpuFreqMHz * 1000000) / (swoFreqMHz * 1000000) - 1;
*(volatile uint32_t *)0xE0040004 = prescaler;
/* 使能 ITM 端口 0 */
*(volatile uint32_t *)0xE0000000 = 0xC5ACCE55; // 解锁 ITM
*(volatile uint32_t *)0xE0000000 = portBits; // 使能对应端口
}
3.5 数据采集:到底能抓到什么?
SWO 输出的数据分几种类型,我列个表方便你对照:
| ITM 端口 | 数据类型 | 典型用途 |
|---|---|---|
| 端口 0 | 软件事件(printf 输出) | 调试日志、变量值打印 |
| 端口 1-31 | 用户自定义事件 | 状态机跳转、中断触发标记 |
| 硬件事件 | 异常、中断、PC 采样 | 分析死机位置、中断延迟 |
| 时间戳 | 全局时间计数 | 测量代码执行时间 |
烧录失败后,我最常用的是 硬件事件 和 PC 采样。举个例子:芯片上电后直接跑飞,你连调试器都连不上。这时候可以先用 SWO 抓 PC 采样,看看 CPU 到底在哪个地址转悠。如果 PC 停在 0xFFFFFFF0 附近,那多半是中断向量表没配好;如果 PC 在某个外设地址区间反复跳,那就是外设时钟没开。
3.6 实战流程:烧录失败后的 SWO 三板斧
我总结了一套流程,每次烧录失败后按这个顺序来,基本能定位 80% 的问题:
- 第一步:检查调试器连接——用 J-Link Commander 或 ST-Link Utility 读芯片 ID。读不到?查硬件连接和供电。
- 第二步:强制复位并抓 SWO 数据——按住复位键,在调试器侧启动 SWO 采集,然后松手。你会看到 CPU 从复位向量开始执行的全过程。
- 第三步:分析异常事件——如果 SWO 输出里出现 HardFault、MemManage、BusFault 等异常号,直接定位到异常向量表,看是哪个外设或指令触发的。
ITM_SendChar('F');。这样只要芯片进异常,SWO 就会吐出一个 'F',你立刻就知道问题出在异常处理上,而不是代码跑飞。
3.7 知识体系:SWO 调试的核心逻辑
下面这张图是我自己整理的 SWO 调试知识框架,帮你快速建立全局认知:
3.8 常见问题与避坑
最后聊几个我实际遇到过的问题,你碰到了可以直接对号入座:
- SWO 没数据? 先查 SWO 引脚是否被复用成 GPIO。很多芯片的 SWO 引脚默认是 GPIO 功能,需要在代码里显式配置为 SWO。
- 数据全是乱码? 波特率不匹配。用逻辑分析仪抓一下 SWO 引脚的波形,数一下脉冲宽度,反推实际波特率。
- 只能抓到部分数据? ITM 端口没全使能。检查
*(volatile uint32_t *)0xE0000000的值,确保你需要的端口位被置 1。 - 调试器连不上芯片? 试试用 J-Link 的「强制连接」模式(
-force参数),有时候芯片处于低功耗模式,普通连接会被拒绝。