4. USART1 复用配置实战:TX(PB6)/RX(PB7) 重映射到 PA9/PA10
好,咱们直接进入正题。这一节要干的事很明确:把 USART1 的 TX 和 RX 从默认的 PB6/PB7,挪到 PA9/PA10 上去。为什么要这么干?说白了就是引脚打架了。我在项目里遇到过好几次,PB6/PB7 被 I2C 或者定时器通道占着,不改映射根本没法同时用。
4.1 重映射到底在干什么?
STM32 的 GPIO 复用功能,不是焊死在某个引脚上的。芯片内部有一套 复用功能选择器,你可以通过寄存器告诉它:USART1_TX 我要从 PA9 出去,而不是 PB6。这就是重映射(Remap)。
核心概念:重映射 ≠ 重新初始化 GPIO。它是在 AFIO(Alternate Function I/O) 时钟使能的前提下,通过 GPIO_PinRemapConfig() 函数切换内部信号路由。
我刚开始学的时候,总以为重映射是改 GPIO 模式或者速度。其实不是,它改的是 外设信号到引脚的连接关系。你想想看,PA9 本身就能作为 USART1_TX,但默认这个功能是关闭的,你得手动打开。
4.2 硬件连接与引脚对应关系
先看表格,把引脚对应关系理清楚。这是调试的基础,焊错线或者配错引脚,后面全白干。
| USART1 信号 | 默认引脚(无重映射) | 重映射后引脚 | 备注 |
|---|---|---|---|
| TX | PB6 | PA9 | 推挽复用输出,50MHz |
| RX | PB7 | PA10 | 浮空输入或上拉输入 |
| CK(时钟) | PB8 | PA8 | 同步模式才用,本例不用 |
注意:重映射后,PB6/PB7 不再承担 USART1 功能。如果你还把它们配成 USART1 模式,信号会冲突,通信直接乱掉。我曾经有个同事就是没关旧引脚的复用,结果 TX 同时从两个脚往外发数据,波形都畸变了。
4.3 配置步骤:从时钟到引脚
配置流程其实就四步,但每一步都有坑。我按顺序列出来,你照着做基本不会出问题。
- 使能 AFIO 时钟 —— 这是重映射的总开关,忘了它,后面所有配置都无效。
- 使能 GPIOA 时钟 —— PA9/PA10 要工作,时钟不能少。
- 调用重映射函数 —— 告诉芯片把 USART1 路由到 PA9/PA10。
- 配置 GPIO 模式 —— TX 配推挽复用,RX 配输入。
嗯,这里要注意:顺序不能乱。我习惯先开 AFIO 时钟,再开 GPIO 时钟,最后做重映射和模式配置。如果你先配了 GPIO 模式再开 AFIO,有时候重映射会不生效,因为内部路由还没建立。
4.4 代码实现:标准库与 HAL 库双版本
下面给两套代码,一套标准库,一套 HAL 库。你自己项目用哪个库就参考哪个。
4.4.1 标准库版本
// 1. 使能 AFIO 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 2. 使能 GPIOA 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 3. 重映射 USART1 到 PA9/PA10
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
// 4. 配置 PA9 为推挽复用输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 5. 配置 PA10 为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
4.4.2 HAL 库版本
// 1. 使能 AFIO 时钟(HAL 库中需手动使能)
__HAL_RCC_AFIO_CLK_ENABLE();
// 2. 使能 GPIOA 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 3. 重映射 USART1
HAL_GPIOEx_RemapConfig(GPIO_Remap_USART1, ENABLE);
// 4. 配置 PA9 为复用推挽
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 5. 配置 PA10 为浮空输入
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
个人习惯:我在 HAL 库中配 RX 引脚时,喜欢把 Pull 设成 GPIO_PULLUP。为什么?因为 USART 空闲时 RX 是高电平,内部上拉能保证电平稳定,防止误触发。虽然浮空也能用,但抗干扰差一点。
4.5 波特率与引脚对应关系验证
配置完了,怎么知道重映射生效了?光看代码编译通过没用,得实测。我一般用两种方法验证:
- 示波器看波形:用探头点 PA9,发一串数据 0x55(二进制 01010101),应该能看到方波。如果 PB6 上有波形而 PA9 上没有,说明重映射没生效。
- 串口助手回环测试:把 PA9 和 PA10 短接,发什么收什么。如果数据正确,说明 TX/RX 通路都通了。
至于波特率验证,说白了就是看波形宽度对不对。比如 115200bps,一个 bit 的宽度是 1/115200 ≈ 8.68μs。示波器上量一下,误差在 ±2% 以内就算正常。我遇到过晶振不准导致波特率偏了 5%,通信时好时坏,查了半天才发现是外部晶振电容配错了。
4.6 重映射配置流程图
下面这张图把整个配置流程串起来了,你调试的时候可以对照着看,卡在哪一步一目了然。
4.7 常见故障与定位方法
配置完了,通信还是不通?别急,我总结了几种最常见的情况,你按顺序排查就行。
| 故障现象 | 可能原因 | 排查方法 |
|---|---|---|
| PA9 无波形 | AFIO 时钟未使能 | 检查 RCC_APB2PeriphClockCmd 或 __HAL_RCC_AFIO_CLK_ENABLE 是否调用 |
| PA9 有波形但数据错乱 | 波特率不匹配或 RX 引脚配置错误 | 示波器量 bit 宽度,检查 PA10 模式是否为输入 |
| PB6 仍有波形 | 重映射未生效或旧引脚未禁用 | 确认 GPIO_PinRemapConfig 参数正确,检查 PB6 是否被其他外设占用 |
| 通信时好时坏 | RX 引脚未上拉或外部干扰 | 将 PA10 改为上拉输入,检查 PCB 走线是否过长 |
重要提醒:如果你用的是 STM32F1 系列,重映射后 必须重新配置 GPIO。有些开发者以为重映射函数会自动配好引脚,结果 TX 还是默认的推挽输出,但内部路由已经改了,信号根本出不去。我踩过这个坑,折腾了俩小时才发现是 GPIO 模式没改。
4.8 调试小技巧:用逻辑分析仪快速验证
没有示波器怎么办?逻辑分析仪也行,而且更便宜。把通道夹在 PA9 上,触发条件设成下降沿,然后发一个字节。看波形:起始位(低电平)、8 个数据位、停止位(高电平)。如果波形完整,说明 TX 通路没问题。
至于 RX,可以用串口助手配合 USB 转 TTL 模块,从 PA10 发数据,看单片机能不能收到。我习惯在中断里点个灯,收到数据就翻转 LED,这样不用接串口打印也能知道通信状态。
嗯,这一节的内容就到这。重映射配置其实不难,关键是顺序和引脚对应关系别搞错。你按我给的步骤走,再配合示波器或逻辑分析仪验证,基本一次过。