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+SWD | JTAG + 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、PB110: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/PB7I2C1_REMAP=1:SCL/PB8, SDA/PB9(不适用于36脚封装)
8.3.10 SPI1 复用功能重映射
SPI1_REMAP=0:NSS/PA4, SCK/PA5, MISO/PA6, MOSI/PA7SPI1_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/PB1ETH_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位,每个位或位域控制一个外设的重映射。
我会把每个位域都列出来解释,不跳过一个。
| 位域 | 名称 | 功能 | 取值含义 |
|---|---|---|---|
| 位0 | SPI1_REMAP | SPI1重映射 | 0:默认(PA4PA7);1:重映射(PA15,PB3PB5) |
| 位1 | I2C1_REMAP | I2C1重映射 | 0:默认(PB6,PB7);1:重映射(PB8,PB9) |
| 位2 | USART1_REMAP | USART1重映射 | 0:默认(PA9,PA10);1:重映射(PB6,PB7) |
| 位3 | USART2_REMAP | USART2重映射 | 0:默认(PA0PA4);1:重映射(PD3PD7) |
| 位5:4 | USART3_REMAP[1:0] | USART3重映射 | 00=默认(PB10PB14);01=部分重映射(PC10PC12);11=完全重映射(PD8~PD12) |
| 位7:6 | TIM1_REMAP[1:0] | TIM1重映射 | 00=默认(PA8~PA12, PB12PB15);01=部分重映射(PA6/PA7/PB0/PB1);11=完全重映射(PE7PE15) |
| 位9:8 | TIM2_REMAP[1:0] | TIM2重映射 | 00=默认(PA0~PA3);01=部分1(PA15,PB3);10=部分2(PB10,PB11);11=完全(PA15,PB3,PB10,PB11) |
| 位11:10 | TIM3_REMAP[1:0] | TIM3重映射 | 00=默认(PA6,PA7,PB0,PB1);10=部分(PB4,PB5);11=完全(PC6~PC9) |
| 位12 | TIM4_REMAP | TIM4重映射 | 0:默认(PB6PB9);1:重映射(PD12PD15) |
| 位14:13 | CAN_REMAP[1:0] | CAN重映射 | 00=默认(PA11,PA12);10=PB8,PB9;11=PD0,PD1 |
| 位15 | PD01_REMAP | PD0/PD1映射到晶振脚 | 0:不映射;1:PD0→OSC_IN, PD1→OSC_OUT(仅小封装) |
| 位16 | TIM5CH4_IREMAP | TIM5_CH4内部重映射 | 0:连接到PA3;1:连接到LSI(用于校准LSI频率) |
| 位21 | ETH_REMAP | 以太网重映射 | 0:默认(PA7,PC4,PC5,PB0,PB1);1:重映射(PB8~PB12) |
| 位22 | CAN2_REMAP | CAN2重映射(互联型) | 0:PB12,PB13;1:PB5,PB6 |
| 位23 | MII_RMII_SEL | MII/RMII接口选择 | 0:MII;1:RMII(以太网PHY接口) |
| 位26:24 | SWJ_CFG[2:0] | 调试接口配置 | 000=全开;001=关JNTRST;010=只保留SWD;100=全关(危险!) |
| 位28 | SPI3_REMAP | SPI3重映射(互联型) | 0:默认(PA15,PB3,PB4,PB5);1:重映射(PA4,PC10,PC11,PC12) |
| 位29 | TIM2ITR1_REMAP | TIM2内部触发1重映射 | 0:连接以太网PTP;1:连接USB OTG SOF(用于时钟校准) |
| 位30 | PTP_PPS_REMAP | 以太网PPS输出重映射 | 0:不输出;1:输出到PB5(秒脉冲信号) |
特别注意:位26:24
SWJ_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位值 | 选择 |
|---|---|
0000 | PAx |
0001 | PBx |
0010 | PCx |
0011 | PDx |
0100 | PEx |
0101 | PFx(仅大容量/互联型) |
0110 | PGx(仅大容量/互联型) |
举例:要把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