4. JTAG接口电路设计(备选方案):TMS/TCK/TDI/TDO连接、JTAG链配置、调试接口复用
JTAG,说白了就是调试和烧录的"最后一道防线"。SWD虽然只用了两根线,但JTAG有它不可替代的地方——边界扫描、多核调试、FPGA联合调试,这些场景下JTAG才是正主。
我做运动控制卡这么多年,遇到过好几次SWD死活连不上,换JTAG一把就通的怪事。所以虽然本章叫"备选方案",但我建议你认真看完。说不定哪天就靠它救场。
4.1 JTAG vs SWD:什么时候选谁?
先看一张对比表,帮你快速决策:
| 特性 | JTAG(4线) | SWD(2线) |
|---|---|---|
| 引脚数 | 4(TMS/TCK/TDI/TDO) | 2(SWDIO/SWCLK) |
| 调试速度 | 最高可达几十MHz | 通常4-10MHz |
| 边界扫描 | ✅ 原生支持 | ❌ 不支持 |
| 多核调试 | ✅ 一条链上挂多个核 | ❌ 单核 |
| FPGA联合调试 | ✅ 标准接口 | ❌ 不适用 |
| PCB占用面积 | 较大(10pin或20pin座) | 小(4pin或5pin座) |
| 典型场景 | 研发调试、产线烧录 | 现场维护、空间受限 |
我的建议:运动控制卡研发阶段,板上同时保留JTAG和SWD焊盘。JTAG用于深度调试,SWD用于日常烧录。量产时根据成本取舍,但至少留一组SWD。
4.2 JTAG信号详解:TMS/TCK/TDI/TDO
JTAG的核心就这四根线,外加一个可选的nTRST(复位)和nSRST(系统复位)。
- TCK(Test Clock):测试时钟,所有操作同步于这个时钟。我习惯在TCK上串一个22Ω电阻,靠近MCU放置,减少反射。
- TMS(Test Mode Select):模式选择,控制JTAG状态机跳转。这个信号在TCK上升沿采样。
- TDI(Test Data In):数据输入,串行移入指令或数据。
- TDO(Test Data Out):数据输出,串行移出数据。注意TDO在TCK下降沿输出数据。
这里有个坑——TDO的驱动能力。我曾经在一个项目里,JTAG线缆拉到了30cm,结果TDO信号变形严重,调试器频繁报错。后来在TDO上加了一个33Ω的串联电阻,问题解决。
经验值:TCK频率建议控制在10MHz以内。超过这个值,PCB走线长度、阻抗匹配、信号完整性都要仔细算。运动控制卡通常不需要那么高的调试频率,4-8MHz足够用。
4.3 JTAG链配置:多器件串联
JTAG链,说白了就是把多个支持JTAG的器件串成一串。TDI进第一个,TDO出最后一个。调试器通过一条链访问所有器件。
运动控制卡上常见的JTAG链成员:
- 主控STM32
- FPGA(如果有)
- CPLD(如果有)
- 某些专用运动控制芯片
链的拓扑结构很简单:
TDI → [器件1 TDI→TDO] → [器件2 TDI→TDO] → ... → [器件N TDI→TDO] → TDO
每个器件在链上有一个唯一的IR(指令寄存器)长度。调试器通过IR扫描识别链上器件。我遇到过一个问题:链上某个器件的IR长度和调试器预期不符,导致整条链无法识别。排查了半天,发现是FPGA的JTAG IP核配置错了。
注意:链上所有器件的TCK和TMS必须并联,不能串联。TDI/TDO才是串联的。这个搞反了,整条链都废了。
4.4 调试接口复用:一针多用
STM32的JTAG引脚和GPIO是复用的。默认复位后,这些引脚是JTAG功能。如果你要用作普通GPIO,需要在代码里关闭JTAG。
复用配置表(以STM32F4为例):
| 引脚 | 默认功能 | 复用功能 | 关闭方式 |
|---|---|---|---|
| PA13 | JTMS/SWDIO | GPIO | DBGMCU->CR 或 AFIO->MAPR |
| PA14 | JTCK/SWCLK | GPIO | 同上 |
| PA15 | JTDI | GPIO/SPI_NSS | 关闭JTAG后可用 |
| PB3 | JTDO | GPIO/SPI_SCK | 关闭JTAG后可用 |
| PB4 | NJTRST | GPIO/SPI_MISO | 关闭JTAG后可用 |
代码示例:关闭JTAG,释放引脚给GPIO
// 关闭全部JTAG功能,保留SWD
// 注意:一旦执行,JTAG调试器将无法连接
// 必须用SWD或重新上电才能恢复
void JTAG_Disable(void)
{
// 对于STM32F4系列
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// 配置PA15(PB3,PB4)为推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 关闭JTAG,保留SWD
// 0x02 = 关闭JTAG-DP,保留SW-DP
// 0x01 = 全部关闭(包括SWD)
HAL_DBGMCU_DisableDBGStopMode();
__HAL_AFIO_REMAP_SWJ_DISABLE(); // 全部关闭
// 或者 __HAL_AFIO_REMAP_SWJ_NOJTAG(); // 只关JTAG,保留SWD
}
血的教训:我曾经在产线固件里加了关闭JTAG的代码,结果有一批板子烧录完就再也连不上了。排查发现是关闭代码执行得太早,调试器还没来得及断开。正确的做法是:在固件启动后延迟几秒再关闭JTAG,给调试器留足断开时间。
4.5 电路设计实战:JTAG接口原理图
下面是我常用的JTAG接口电路,20pin标准座,兼容ARM 20pin定义:
// JTAG 20pin接口连接(ARM标准)
// 引脚号 信号名 连接目标
// 1 VREF 3.3V(通过10kΩ上拉)
// 2 nSRST MCU的NRST(通过10kΩ上拉)
// 3 TDI MCU的PA15(串22Ω电阻)
// 4 nTRST MCU的PB4(通过10kΩ上拉)
// 5 TMS MCU的PA13(串22Ω电阻)
// 6 GND GND
// 7 TCK MCU的PA14(串22Ω电阻)
// 8 GND GND
// 9 TDO MCU的PB3(串33Ω电阻)
// 10 GND GND
// 11-20 GND GND(或NC)
关键设计要点:
- 上拉电阻:TMS、TDI、nTRST、nSRST各加一个10kΩ上拉到3.3V。TCK和TDO不需要上拉。
- 串联电阻:TDI、TMS、TCK串22Ω,TDO串33Ω。靠近MCU引脚放置。
- ESD保护:如果接口要引出到机箱外部,加TVS管(如PESD5V0S1UB)。
- 滤波电容:VREF引脚对地加100nF电容。
我的习惯:在JTAG接口旁边加一个LED指示灯,通过MCU的GPIO控制。调试时亮起,烧录完成后熄灭。产线工人一眼就能看出烧录状态,不用看电脑屏幕。
4.6 JTAG状态机与调试流程
JTAG的核心是一个16状态的有限状态机。你不需要背下所有状态,但要知道几个关键路径:
- Test-Logic-Reset:上电默认状态,TMS保持高5个TCK周期进入此状态。
- Run-Test/Idle:空闲状态,等待指令。
- Shift-IR:移入指令。
- Shift-DR:移入数据。
调试器通过TMS控制状态跳转。每次跳转都在TCK上升沿采样TMS。这就是为什么TMS和TCK的时序必须干净——任何毛刺都会导致状态机跑飞。
下面这张图展示了JTAG状态机的核心路径:
4.7 避坑指南:JTAG设计常见问题
- 信号反射:TCK走线过长或阻抗不匹配,导致信号过冲。解决方案:串22-33Ω电阻,走线控制在10cm以内。
- 电平不匹配:调试器是5V,MCU是3.3V。必须加电平转换,否则烧MCU。我推荐TXS0104E或分立MOS管方案。
- 上电时序:MCU还没上电,调试器先给了JTAG信号,可能导致MCU锁死。解决方案:在JTAG接口上加一个延迟上电电路,或者用调试器控制nTRST。
- 地线回路:调试器和目标板地电位差过大,导致通信不稳定。解决方案:用粗地线连接,或者加隔离器(如ADuM1201)。
- 引脚复用冲突:JTAG引脚被其他外设占用,调试时冲突。解决方案:设计阶段就规划好引脚分配,JTAG引脚尽量不与其他功能复用。
总结一下:JTAG接口设计,说白了就是四根信号线的物理层和时序层设计。物理层做好上拉、串联电阻、ESD保护;时序层保证TCK干净、TMS稳定。做到这两点,JTAG基本不会出问题。运动控制卡对调试接口的可靠性要求高,我建议你花点心思在PCB布局上——JTAG走线远离电机驱动、PWM等强干扰源。