stm32GPIO复用功能与重映射

stm32GPIO复用功能与重映射

本文系统讲解 STM32 的 AFIO 复用功能与重映射机制,覆盖术语、数据手册 8.3/8.4 节解读、寄存器位域详解及六步配置流程。

目录

  • 1. 术语解释
  • 2.参考数据手册8.3节,复用功能I/O和调试配置
    • 8.3 开篇总览
    • 8.3.1 把 OSC32_IN/OSC32_OUT 作为 GPIO 端口 PC14/PC15
    • 8.3.2 把 OSC_IN/OSC_OUT 引脚作为 GPIO 端口 PD0/PD1
    • 8.3.3 CAN1 复用功能重映射
    • 8.3.4 CAN2 复用功能重映射(仅互联型产品)
    • 8.3.5 JTAG/SWD 复用功能重映射
    • 8.3.6 ADC 复用功能重映射
    • 8.3.7 定时器复用功能重映射
    • 8.3.8 USART 复用功能重映射
    • 8.3.9 I²C1 复用功能重映射
    • 8.3.10 SPI1 复用功能重映射
    • 8.3.11 SPI3 复用功能重映射(仅互联型)
    • 8.3.12 以太网复用功能重映射(仅互联型STM32F107)
  • 3. 参考数据手册8.4节,AFIO寄存器描述
    • 8.4.1 事件控制寄存器 (AFIO_EVCR)
    • 8.4.2 复用重映射和调试I/O配置寄存器 (AFIO_MAPR)
    • 8.4.3 ~ 8.4.6 外部中断配置寄存器 (AFIO_EXTICR1~4)
  • 4. 使用步骤 —— 手打代码
    • 4.1 骨架
      • 步骤 1
      • 步骤 2
      • 步骤 3:释放调试引脚(如目标为 PA15 / PB3 / PB4)
      • 步骤 4:配置 GPIO 复用模式
      • 步骤 5:执行重映射 / 选择外部中断引脚
      • 步骤 6:初始化外设 / 中断

1. 术语解释

在进入正文之前,请务必先理解下面这些词的意思,否则后面的内容你会越看越懵。

术语通俗解释重点
GPIO芯片的引脚,可以当输入或输出用普通数字IO,电平由寄存器控制
AFIO引脚功能“调度中心”,负责换引脚和选外部中断引脚用之前必须先开 AFIO 时钟,否则配置无效
复用功能(AF)让引脚去干外设的活(比如串口发送、SPI时钟)信号直接连到外设,不经过 GPIO 数据寄存器
重映射(Remap)把外设的引脚从默认位置“搬”到备用位置AFIO_MAPR寄存器,每个外设有对应开关位
调试端口(JTAG/SWD)默认占用 PA13、PA14、PA15、PB3、PB4 用于调试想释放这些脚,设置SWJ_CFG=010只保留 SWD,千万别设 100
寄存器芯片内部的控制开关,每个位管一件事所有配置最终都是读写寄存器
外部中断线(EXTI Line)16 根中断线,每根线同一时刻只能接一个引脚选引脚查AFIO_EXTICR,每 4 位选一个端口

2.参考数据手册8.3节,复用功能I/O和调试配置

  • 8.3节就是一张功能迁移表,它告诉你哪些外设(USART、SPI、I2C、CAN、TIM、ADC、ETH)可以换脚,以及可以换到哪一组新引脚上。
    你不需要背下每个外设的映射组合,但你需要知道:当你的默认引脚被占用时,可以来这里查表,看看有没有替代方案

8.3 开篇总览

“为了优化64脚或100脚封装的外设数目,可以把一些复用功能重新映射到其他引脚上。设置复用重映射和调试I/O配置寄存器(AFIO_MAPR)实现引脚的重新映射。这时,复用功能就不再映射到它们的原始分配上了。”

解释
芯片设计者知道,很多引脚不够用或者布线不方便。所以他们在硬件上留了后门——允许你把某些外设的默认引脚换成另一组空闲引脚。怎么换?就是通过设置AFIO_MAPR这个寄存器。一旦换了,原来的默认引脚就不再承担这个外设的功能了。


8.3.1 把 OSC32_IN/OSC32_OUT 作为 GPIO 端口 PC14/PC15

“当LSE振荡器关闭时,LSE振荡器引脚OSC32_IN/OSC32_OUT可以分别用做GPIO的PC14/PC15,LSE功能始终优先于通用I/O口的功能。”

  • 解释:PC14和PC15这两个引脚,默认是接外部32.768kHz晶振(给RTC用的)的。如果你不用外部晶振(改用内部LSI),你就可以把这两个脚解放出来当普通IO口用。
  • 手册后面备注:当1.8V核心电压关闭(进入待机模式)或者后备区域由VBAT供电而VDD断电时,PC14/PC15不能当GPIO用。另外,这两个脚输出能力很弱,最大驱动电流只有3mA,不能用来推LED,速度也必须限制在2MHz以下。

8.3.2 把 OSC_IN/OSC_OUT 引脚作为 GPIO 端口 PD0/PD1

“外部振荡器引脚OSC_IN/OSC_OUT可以用做GPIO的PD0/PD1,通过设置复用重映射和调试I/O配置寄存器(AFIO_MAPR)实现。这个重映射只适用于36、48和64脚的封装(100脚和144脚的封装上有单独的PD0和PD1的引脚,不必重映射)”

  • 解释:PD0和PD1默认是接外部高速晶振(HSE)的。如果你用内部RC振荡器(HSI),不需要外部晶振,那么你就可以把这两个脚当普通IO口。但注意,大封装(100脚以上)本身就自带PD0/PD1,不需要重映射,直接就能用。只有小封装才需要通过设置AFIO_MAPR来启用。
  • 手册后面备注外部中断功能不会被重映射,也就是说在36/48/64脚封装上,你无法用PD0/PD1产生外部中断。

8.3.3 CAN1 复用功能重映射

CAN信号可以被映射到端口A、端口B或端口D上,如下表所示。…CAN_REMAP[1:0]=“00”:CAN1_RX/PA11, CAN1_TX/PA12;“10”:CAN1_RX/PB8, CAN1_TX/PB9;“11”:CAN1_RX/PD0, CAN1_TX/PD1。

  • 解释:CAN1这个外设,默认在PA11和PA12上。如果你不想用这两个脚,可以改成PB8/PB9,或者PD0/PD1。通过设置AFIO_MAPR寄存器的位[14:13](即CAN_REMAP)来选择。
  • 注意:重映射到PD0/PD1只适用于100脚和144脚的大封装,因为只有这些封装才有独立的PD0/PD1引脚(不会被当作晶振脚)。

8.3.4 CAN2 复用功能重映射(仅互联型产品)

在互联型产品中还有CAN2接口,它的外部信号可以按下表重映射:CAN2_REMAP=“0” -> CAN2_RX/PB12, CAN2_TX/PB13;CAN2_REMAP=“1” -> CAN2_RX/PB5, CAN2_TX/PB6。

  • 解释:只有STM32F105/107(互联型)才有CAN2。默认在PB12/PB13,也可以通过设置重映射位,把它们搬到PB5/PB6去。

8.3.5 JTAG/SWD 复用功能重映射

为了在调试期间可以使用更多GPIOs,通过设置复用重映射和调试I/O配置寄存器(AFIO_MAPR)的SWJ_CFG[2:0]位,可以改变上述重映像配置。

  • 背景:默认情况下,PA13(JTMS/SWDIO)、PA14(JTCK/SWCLK)、PA15(JTDI)、PB3(JTDO)、PB4(JNTRST)都被用作调试接口。如果你想把这些脚省出来做普通IO,就要修改SWJ_CFG位。

表35 调试端口映像

SWJ_CFG[2:0]效果可用调试口被释放的引脚
000(复位默认)全部引脚用作JTAG+SWDJTAG + SWD
001全部引脚用作JTAG+SWD,但PB4(JNTRST)被释放JTAG + SWD(无复位)PB4
010关闭JTAG,只保留SWD仅SWD(PA13/PA14)PA15、PB3、PB4
100彻底关闭所有调试口无(再也烧不进程序)全部释放

重点:除非你确定以后永远不用调试器烧录了,否则绝不要设置100。一旦设了,你的芯片就再也连接不上调试器,只能通过串口ISP擦除后才能恢复,新手很容易以为是芯片坏了。日常开发请使用010,只释放PA15/PB3/PB4,保留最常用的SWD(PA13/PA14)。


8.3.6 ADC 复用功能重映射

ADC1外部触发注入转换…ADC1_ETRGIN_REMAP=0:与EXTI15相连;=1:与TIM8_CH4相连。
ADC1外部触发规则转换…ADC1_ETRGEI_REMAP=0:与EXTI11相连;=1:与TIM8_TRGO相连。

  • 解释:ADC的规则转换和注入转换,原本可以用外部中断线EXTI11/EXTI15来触发。通过重映射,可以把触发源改成高级定时器TIM8的某些信号(TIM8_CH4或TIM8_TRGO)。这样你就可以用定时器来精确控制ADC采样的时刻。这个功能只在大容量产品(256KB~512KB Flash)上才有

8.3.7 定时器复用功能重映射

这一节列出了多个定时器(TIM1~TIM5)的通道映射选项。我挑几个典型的来解释:

  • TIM4_REMAP:默认 TIM4_CH1~CH4 在 PB6~PB9;设为1后,可以搬到 PD12~PD15(仅100/144脚封装)。
  • TIM3_REMAP[1:0]
    • 00:PA6、PA7、PB0、PB1
    • 10:PB4、PB5、PB0、PB1(部分重映射)
    • 11:PC6、PC7、PC8、PC9(完全重映射,仅64/100/144脚封装)
  • TIM2_REMAP[1:0]:可以选择把CH1_ETR从PA0换到PA15,把CH2从PA1换到PB3,把CH3/CH4换到PB10/PB11等等。
  • TIM1_REMAP[1:0]:可以重映射刹车输入(BKIN)、外部触发(ETR)以及互补输出通道(CHxN),例如把BKIN从PB12搬到PA6或PE15。

实战意义:比如你做电机控制,TIM1的互补PWM输出默认在PA8~PA15等脚,如果这些脚被其他外设占用,你可以把它们整体搬到PE(大封装)或者用部分重映射搬到其他位置。


8.3.8 USART 复用功能重映射

  • USART1_REMAP:0 -> TX/PA9, RX/PA10;1 -> TX/PB6, RX/PB7。
  • USART2_REMAP:0 -> PA0~PA4;1 -> PD3~PD7(仅100/144脚)。
  • USART3_REMAP[1:0]
    • 00:PB10/PB11/PB12/PB13/PB14(TX/RX/CK/CTS/RTS)
    • 01:PC10/PC11/PC12 + PB13/PB14(部分重映射,64/100/144脚)
    • 11:PD8/PD9/PD10/PD11/PD12(完全重映射,100/144脚)

使用场景:画PCB时发现串口默认引脚走线太绕,就可以查这张表换到更顺的引脚上去。


8.3.9 I²C1 复用功能重映射

  • I2C1_REMAP=0:SCL/PB6, SDA/PB7
  • I2C1_REMAP=1:SCL/PB8, SDA/PB9(不适用于36脚封装)

8.3.10 SPI1 复用功能重映射

  • SPI1_REMAP=0:NSS/PA4, SCK/PA5, MISO/PA6, MOSI/PA7
  • SPI1_REMAP=1:NSS/PA15, SCK/PB3, MISO/PB4, MOSI/PB5

8.3.11 SPI3 复用功能重映射(仅互联型)

  • SPI3_REMAP=0:NSS/PA15, SCK/PB3, MISO/PB4, MOSI/PB5(和SPI1重映射后的引脚冲突,小心!)
  • SPI3_REMAP=1:NSS/PA4, SCK/PC10, MISO/PC11, MOSI/PC12

注意:SPI3_NSS和JTDI共享PA15,SPI3_SCK和JTDO共享PB3,所以如果你要用SPI3,通常要先关掉JTAG(设置SWJ_CFG=010)释放PA15和PB3。


8.3.12 以太网复用功能重映射(仅互联型STM32F107)

  • ETH_REMAP=0:RX_DV/PA7, RXD0/PC4, RXD1/PC5, RXD2/PB0, RXD3/PB1
  • ETH_REMAP=1:RX_DV/PB8, RXD0/PB9, RXD1/PB10, RXD2/PB11, RXD3/PB12

用于以太网MAC的接收数据线重映射,方便PCB走线。


3. 参考数据手册8.4节,AFIO寄存器描述

8.4节告诉你“怎么用代码(操作寄存器)来实现8.3里的重映射。它详细列出了几个寄存器的每一位分别控制什么。

超级重要的前提
“对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO的时钟。”

  • 标准库写法RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  • 寄存器写法RCC->APB2ENR |= (1<<0);(因为AFIOEN在RCC_APB2ENR的第0位)

8.4.1 事件控制寄存器 (AFIO_EVCR)

这个寄存器控制一个非常冷门的功能:把Cortex‑M3内部的EVENTOUT信号输出到某个外部GPIO引脚上。

寄存器位详解

  • EVOE(位7):使能事件输出。为1时,允许把EVENTOUT连接到选定的IO口。
  • PORT[2:0](位6:4):选择输出到哪个端口(000=PA, 001=PB, 010=PC, 011=PD, 100=PE)。
  • PIN[3:0](位3:0):选择输出到该端口的哪个引脚(0000=Px0,……,1111=Px15)。

实际应用:绝大多数应用根本用不到这个寄存器,99%的代码里都不会出现。你可以直接忽略它,只需要知道它是用来把一个内部调试/同步信号引出来而已。


8.4.2 复用重映射和调试I/O配置寄存器 (AFIO_MAPR)

这是本节的核心!它包含了8.3节里提到的所有重映射开关。整个寄存器共32位,每个位或位域控制一个外设的重映射。

我会把每个位域都列出来解释,不跳过一个。

位域名称功能取值含义
位0SPI1_REMAPSPI1重映射0:默认(PA4PA7);1:重映射(PA15,PB3PB5)
位1I2C1_REMAPI2C1重映射0:默认(PB6,PB7);1:重映射(PB8,PB9)
位2USART1_REMAPUSART1重映射0:默认(PA9,PA10);1:重映射(PB6,PB7)
位3USART2_REMAPUSART2重映射0:默认(PA0PA4);1:重映射(PD3PD7)
位5:4USART3_REMAP[1:0]USART3重映射00=默认(PB10PB14);01=部分重映射(PC10PC12);11=完全重映射(PD8~PD12)
位7:6TIM1_REMAP[1:0]TIM1重映射00=默认(PA8~PA12, PB12PB15);01=部分重映射(PA6/PA7/PB0/PB1);11=完全重映射(PE7PE15)
位9:8TIM2_REMAP[1:0]TIM2重映射00=默认(PA0~PA3);01=部分1(PA15,PB3);10=部分2(PB10,PB11);11=完全(PA15,PB3,PB10,PB11)
位11:10TIM3_REMAP[1:0]TIM3重映射00=默认(PA6,PA7,PB0,PB1);10=部分(PB4,PB5);11=完全(PC6~PC9)
位12TIM4_REMAPTIM4重映射0:默认(PB6PB9);1:重映射(PD12PD15)
位14:13CAN_REMAP[1:0]CAN重映射00=默认(PA11,PA12);10=PB8,PB9;11=PD0,PD1
位15PD01_REMAPPD0/PD1映射到晶振脚0:不映射;1:PD0→OSC_IN, PD1→OSC_OUT(仅小封装)
位16TIM5CH4_IREMAPTIM5_CH4内部重映射0:连接到PA3;1:连接到LSI(用于校准LSI频率)
位21ETH_REMAP以太网重映射0:默认(PA7,PC4,PC5,PB0,PB1);1:重映射(PB8~PB12)
位22CAN2_REMAPCAN2重映射(互联型)0:PB12,PB13;1:PB5,PB6
位23MII_RMII_SELMII/RMII接口选择0:MII;1:RMII(以太网PHY接口)
位26:24SWJ_CFG[2:0]调试接口配置000=全开;001=关JNTRST;010=只保留SWD;100=全关(危险!)
位28SPI3_REMAPSPI3重映射(互联型)0:默认(PA15,PB3,PB4,PB5);1:重映射(PA4,PC10,PC11,PC12)
位29TIM2ITR1_REMAPTIM2内部触发1重映射0:连接以太网PTP;1:连接USB OTG SOF(用于时钟校准)
位30PTP_PPS_REMAP以太网PPS输出重映射0:不输出;1:输出到PB5(秒脉冲信号)

特别注意:位26:24SWJ_CFG是重中之重。请牢记设置010(只保留SWD),既能调试又能释放PA15/PB3/PB4,还不会锁死芯片。绝对不要设置100


8.4.3 ~ 8.4.6 外部中断配置寄存器 (AFIO_EXTICR1~4)

这四个寄存器(EXTICR1~4) 负责把GPIO引脚映射到外部中断线(EXTI0~EXTI15)

  • 每个寄存器控制4条中断线(例如EXTICR1控制EXTI0~EXTI3 , EXTICR2控制EXTI4~EXTI7,以此类推)。
  • 每条中断线需要4个位来选择引脚来源:
4位值选择
0000PAx
0001PBx
0010PCx
0011PDx
0100PEx
0101PFx(仅大容量/互联型)
0110PGx(仅大容量/互联型)

举例:要把PB0接到EXTI0,就需要在AFIO_EXTICR1的低4位(即EXTI0[3:0])写入0001

注意:只有相应的GPIO端口时钟已经使能(RCC_APB2ENR里打开IOPxEN),并且该引脚被配置为输入模式,外部中断才能正常工作。


4. 使用步骤 —— 手打代码

这一部分我把从“拿到一个项目”到“完成重映射或外部中断配置”的全过程,拆解成 6个步骤

4.1 骨架

开始 │ ├─ 步骤1:查阅手册/数据手册(确认我的芯片封装支持这个重映射吗?) │ ├─ 步骤2:开启RCC时钟(外设时钟 + AFIO时钟,缺一不可!) │ ├─ 步骤3:处理调试端口(如果目标引脚被JTAG/SWD占了,先释放!) │ ├─ 步骤4:配置GPIO底层属性(速度、模式、上拉/下拉) │ ├─ 步骤5:写入AFIO寄存器(执行重映射 或 选择外部中断引脚) │ ├─ 步骤6:初始化外设本身(初始化USART/SPI/TIM 或 配置NVIC中断) │ └─ 结束

步骤 1

查阅参考手册 8.3 节,确认目标外设支持的重映射选项、封装限制及引脚冲突。

步骤 2

// USART1 重映射至 PB6/PB7:开 GPIOB、USART1、AFIO 时钟RCC->APB2ENR|=(1<<0)// AFIOEN|(1<<3)// IOPBEN|(1<<14);// USART1EN

步骤 3:释放调试引脚(如目标为 PA15 / PB3 / PB4)

// SWJ_CFG = 010:关闭 JTAG,保留 SWDAFIO->MAPR=(AFIO->MAPR&~(0x7<<24))|(0x2<<24);

步骤 4:配置 GPIO 复用模式

// PB6:复用推挽输出,50MHz;PB7:浮空输入GPIOB->CRL&=~(0xFF<<24);GPIOB->CRL|=(0x0B<<24)// PB6: CNF=10, MODE=11|(0x04<<28);// PB7: CNF=01, MODE=00

步骤 5:执行重映射 / 选择外部中断引脚

// 场景 A:USART1 重映射至 PB6/PB7AFIO->MAPR|=(1<<2);// USART1_REMAP// 场景 B:PB0 映射至 EXTI0AFIO->EXTICR[0]=(AFIO->EXTICR[0]&~0x000F)|0x0001;// EXTI0 = PB0

步骤 6:初始化外设 / 中断

// USART1:72MHz PCLK2,115200-8-N-1USART1->BRR=(625<<4);// 72MHz / 115200 = 625USART1->CR2=0;// 1 停止位USART1->CR1=(1<<13)// UE|(1<<3)// TE|(1<<2);// RE

若配置外部中断(PB0 下降沿触发):

EXTI->FTSR|=(1<<0);// 下降沿触发EXTI->IMR|=(1<<0);// 使能 EXTI0NVIC->ISER[0]=(1<<6);// EXTI0_IRQn = 6