拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32释放PB3/PB4:禁用JTAG保留SWD的完整指南

简介STM32开发者常会遇到一个现象在原理图设计阶段已经占用PB3和PB4但程序里怎么操作电平都没有反应原因往往在于芯片上电后默认启用JTAG调试接口PB3、PB4乃至PA15都已被复用而非普通IO口。这份PDF正是围绕这个痛点进行拆解从芯片引脚复用原理入手先说明JTAG与SWD的引脚占用差异再给出开启AFIO时钟、调用引脚重映射功能禁用JTAG并保留SWD、将PB3和PB4初始化为推挽输出的关键配置流程同时补充不同型号芯片在寄存器细节上的差异、关闭JTAG后对硬件调试链路的影响并附有对应的代码示例便于直接对照修改。资源规模轻量仅1个PDF文件约102KB适合快速阅读和随查随用。目前已有6768人学习下载说明该问题在项目中确实高频出现。对于IO口紧张、需要复用调试引脚的设计这份经验笔记能帮助快速定位问题、规避手册遗漏减少反复试错成本这类冲突不只在PB3和PB4上出现熟悉排查思路后也可迁移到PA15等其他复用引脚的场景尤其适合开发后期需要释放调试引脚以缓解IO压力的项目实用性较强。1. 原理图用了PB3和PB4程序里却控制不了画原理图时为了省IO我把PB3和PB4分配给了外部设备焊完板子正准备调试结果发现无论怎么写GPIO寄存器这两个引脚都没有反应。一开始怀疑芯片坏查了STM32参考手册才发现芯片上电后默认把PB3、PB4、PA15分配给JTAG接口普通GPIO功能根本没有生效。这不是粗心而是对STM32引脚复用机制了解不够。类似的问题在网上很常见搜“stm32禁用jtag”能搜到一堆讨论但真正把原理、代码、排错串起来的很少。这篇文章就用标准外设库为例把PB3和PB4从JTAG占用中释放出来同时覆盖SWD调试器失联、重映射参数选择、以及验证方法这些实际开发中必须面对的细节。适合正在做硬件设计、IO口资源紧张又不想放弃在线调试的工程师。2. JTAG引脚占用机制与AFIO时钟使能逻辑2.1 为什么默认是JTAG而不是普通GPIOSTM32F1系列复位后SWJSerial Wire JTAG接口默认全部使能。JTAG占用5个引脚PB3JTDO、PB4NJTRST、PA15JTDI、PA13SWDIO、PA14SWCLK。SWD模式只需要PA13和PA14。所以只要芯片一上电PB3和PB4这些引脚就被Debug模块接管GPIO控制信号到不了引脚。SWJ功能状态可用引脚说明JTAG SWD默认PA13 / PA14 / PA15 / PB3 / PB4复位后的初始状态占用最多IO关闭JTAG保留SWDPA13 / PA14本方案目标仍可在线调试全部关闭无所有SWJ引脚释放但调试器无法连接只有通过GPIO引脚重映射把SWJ配置从“JTAGSWD”切换到“仅SWD”PB3、PB4、PA15才会释放成普通IO。这里的关键是“关闭JTAG不等于关闭SWD”PA13和PA14仍然留给调试器这也是为什么我们可以放心释放其他三个引脚。2.2 释放PB3/PB4的标准外设库代码常见做法是用标准外设库的三步式配置先开AFIO时钟再重映射SWJ状态最后初始化GPIO。RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_ResetBits(GPIOB, GPIO_Pin_3 | GPIO_Pin_4);这段代码的逻辑是把GPIOB和AFIO的时钟同时打开因为GPIO_PinRemapConfig操作的是AFIO_MAPR寄存器而该寄存器挂在AFIO总线上。GPIO_Remap_SWJ_JTAGDisable是一个宏定义对应AFIO_MAPR寄存器SWJ_CFG字段的“010”状态也就是关闭JTAG、保留SWD。配置完重映射后PB3和PB4才真正回到GPIO外设的控制范围后面再配置为推挽输出就顺理成章了。参数上需要注意GPIO_Mode_Out_PP适合驱动LED、继电器控制信号这类常规数字输出如果外部电路需要开漏改成GPIO_Mode_Out_OD如果复用为输入改成GPIO_Mode_IPU或GPIO_Mode_IPD。GPIO_Speed_50MHz是最大翻转速率并非强制跑50MHz普通信号用这个值没有副作用。2.3 为什么一定要先开AFIO时钟有些刚接触的人会跳过RCC_APB2PeriphClockCmd那行结果发现GPIO_PinRemapConfig并没有生效引脚还是不受控。原因很简单AFIO外设的时钟没使能时对AFIO_MAPR寄存器的写入是无效的甚至在某些型号上会产生总线错误。标准外设库的GPIO_PinRemapConfig内部会对AFIO_MAPR做读改写操作如果AFIO没有时钟读出来就是复位值写回去也不会改变状态。因此正确顺序是使能AFIO和GPIOB时钟然后是重映射最后初始化GPIO。顺序不能反过来。还有一个容易忽视的细节STM32F1的AFIO只有一个挂在APB2总线上不像GPIO那样有ABCDE之分所以不需要像配置多个GPIO那样重复使能。提示禁用JTAG后PA15也会被同步释放。如果你暂时不打算使用PA15建议把它配置为高阻输入或固定输出避免悬空引脚引入不稳定电平。3. PB3/PB4释放实战完整函数、调试器失联与验证3.1 可直接用的初始化函数把上一章的代码封装成函数放在main函数早期调用。我一般会在GPIO时钟初始化之后、任何外设使用PB3/PB4之前执行。void PB3_PB4_Release_Init(void) { // 1. 打开AFIO和GPIOB时钟AFIO用于引脚重映射 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE); // 2. 关闭JTAG功能保留SWD调试引脚PA13/PA14 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); // 3. 初始化PB3/PB4为推挽输出最大速率50MHz GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 4. 初始输出低电平避免外部设备误动作 GPIO_ResetBits(GPIOB, GPIO_Pin_3 | GPIO_Pin_4); }这里我刻意用了GPIO_Pin_3 | GPIO_Pin_4而不是常见教程里的GPIO_Pin_All。GPIO_Pin_All会重新初始化GPIOB的16个引脚如果同端口其他引脚已被设置为模拟输入、复用功能再用这行代码就会把它们全部覆盖成普通推挽输出等于挖了个大坑。明确指定引脚能避免这种“全端口重置”带来的副作用。调用位置也有讲究。我习惯在系统时钟初始化后立刻调用例如在main函数开头int main(void) { SystemInit(); PB3_PB4_Release_Init(); // 其他外设初始化 // ... }这样做的原因是越早重映射PB3/PB4被其他外设误配置的概率越低。如果等到所有外设都初始化完再释放某些依赖PB3/PB4电平的外设可能会在上电瞬间读到不确定状态。3.2 最常见的失败现象SWD/JTAG communication failure在STM32开发中禁用JTAG后遇到最多的错误是调试器连接失败报错类似“SWD/JTAG Communication Failure”。这种现象通常是两个原因之一。第一个原因把GPIO_Remap_SWJ_JTAGDisable错写成GPIO_Remap_SWJ_Disable。这两者差别是致命的前者只关JTAG保留SWD后者把JTAG和SWD全部关闭。一旦全部关闭PA13和PA14也不再服务于调试器芯片瞬间和电脑失去联系。遇到这种情况恢复手段是把BOOT0拉高通过ISP串口下载一个修复固件或者在Keil里使用Cortex-M系列的全擦除功能如果有支持SWD的下载器在BOOT01状态下可以重新连接但前提是芯片ISP没有被禁用。第二个原因调试器设置里还在用JTAG模式连接。禁用JTAG后整个SWJ端口已经只剩SWD功能Keil的Debug设置里如果还选Port为JTAG自然连不上。解决办法是在Debug → Settings里把Port改成SW速度可以降到1MHz左右连接成功后恢复默认值。错误操作现象恢复办法使用GPIO_Remap_SWJ_Disable调试器完全找不到芯片BOOT01ISP串口下载修复程序调试器仍设为JTAG模式Keil识别不到ID下载失败在Debug Settings中把Port改为SW先初始化GPIO后重映射PB3/PB4仍异常调整代码顺序先重映射后GPIO初始化AFIO时钟未使能重映射无效引脚状态不变检查RCC_APB2PeriphClockCmd3.3 如何验证PB3、PB4已经被释放最直观的方法是配置成输出后把引脚拉高用万用表测量引脚对地电压。释放成功的引脚会稳定输出与VCC一致的电平如果还是被JTAG占用电压会很弱或随机跳变。也可以用软件方式验证。把PB3和PB4配置为上拉输入读取IDR寄存器。配置成输入模式后外部不接任何负载正常引脚会读回高电平GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_3 | GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOB, GPIO_InitStructure); uint8_t val3 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3); uint8_t val4 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);注意GPIO_ReadInputDataBit只能传单个引脚不能写成GPIO_Pin_3 | GPIO_Pin_4否则库函数内部按位取“或”的结果会导致判断出错。两个变量分别读两次才能准确获取各自状态。如果读到的都是高电平说明引脚已经被释放并且能正常响应GPIO模块的控制。还可以通过查看AFIO寄存器确认读AFIO-MAPR的Bit[26:24]。SWJ_CFG010表示JTAG已关闭、SWD保持。这一位是只读配置模式吗实际上是可写的但重映射函数已经写好了读出来就能确认状态。4. 进阶让PB3/PB4用得更稳顺带避开PA15的坑释放PB3/PB4之后还有几个值得留意的点。第一启用重映射后PA15也变成了普通IO。很多人在初始化PB3/PB4时忘了处理PA15导致这个引脚处于浮空输入状态在恶劣环境下可能产生额外功耗或干扰。我的一般做法是如果PA15没有实际作用就把它一并配置为推挽输出并输出低电平。这样虽然多花两行代码但能避免不可控的浮空电平。第二如果在主程序里需要频繁翻转PB3/PB4建议使用位带操作或者直接操作BRR和BSRR寄存器而不是用GPIO_ResetBits加GPIO_SetBits的组合。因为GPIO_SetBits内部会有读改写的过程连续翻转时性能不如直接写BSRR。但这只是性能优化不影响正确性。第三对于GD32F4这类Cortex-M4内核的芯片关闭JTAG引脚的机制与STM32F1标准外设库不同不能直接照搬GPIO_PinRemapConfig。正确做法是查对应芯片参考手册里的SWJ_CFG寄存器GD32F4通常需要操作AFIO或SYSCFG区域的某个位。上板之前先确认芯片型号有些工程师一片代码到处复制最后把F1的例子用在F4上结果是编译过了但引脚没有任何反应白白浪费时间。第四如果你还在用J-Link调试器建议直接改用SWD模式连接。SWD只需要SWDIO、SWCLK、GND、VCC四根线在J-Link的接口定义中对应找到这几个焊盘即可。SWD模式下载速度不慢而且释放出来的JTAG引脚可以放心使用不用在硬件上做跳线切换。对于IO口紧张的项目这是最双赢的方案。第五也是我最近才真正理解的一个细节GPIO_PinRemapConfig执行完重映射后通常会建议加两个空指令或短暂延时。虽然从芯片逻辑上看重映射在写寄存器的瞬间已经生效但GPIO模块和调试模块之间的切换需要几个时钟周期的总线同步。我习惯在这行代码后加__NOP()然后再开始操作PB3/PB4并且在示波器上观察过这样的初始化顺序不会在引脚上产生毛刺。程序里的处理如下GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); __NOP(); __NOP();在STM32F103上实测这两条空指令不会影响重映射结果却能让后续读取PB3引脚状态的逻辑更可靠。如果你的代码里有“初始化完成后立刻读引脚电平”的逻辑建议加上这个操作。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门