一、串口通信 基础知识
1. 什么是串口通信
串口是 单片机与外界通信最简单、最常用的方式 。它通过 两根线(TX 发送、RX 接收) 实现数据互相传输:
- 单片机 → 电脑 / 其他模块:发送数据(TX)
- 电脑 / 其他模块 → 单片机:接收数据(RX)
一句话理解:串口就是单片机的 "嘴巴" 和 "耳朵",能发数据、能听数据。
2. 串口通信的作用
- 程序调试(打印日志)
- 与蓝牙、WiFi、GPS 模块通信
- 与上位机(电脑)交互
- 单片机之间互相通信
3. 串口通信的关键参数
必须收发双方完全一致,否则乱码:
- 波特率:通信速度(常用 9600、115200)
- 数据位:8 位(标准)
- 停止位:1 位
- 校验位:无校验(None)
- 流控制:无
4. STM32 串口资源
- STM32F103 有 3 个串口:USART1、USART2、USART3
- 默认引脚:
- USART1:TX=PA9,RX=PA10
- USART2:TX=PA2,RX=PA3
- USART3:TX=PB10,RX=PB11
最常用:USART1(PA9、PA10)
5. 串口通信工作流程
电脑发数据 → RX 引脚收到 → 进入接收中断 → 读取数据单片机发数据 → TX 引脚发出 → 电脑接收显示
二、复用功能(串口必须用复用功能)
1. 什么是 GPIO 复用功能
一个 GPIO 引脚不只是输入 / 输出,还能变成:
- 串口收发
- 定时器 PWM
- SPI、I2C
- 外部中断
这种多功能切换 就叫 复用功能。
2. 为什么串口必须用复用功能
TX、RX 不是普通输入输出,而是 串口外设专用功能引脚,必须开启复用模式。
3. 串口关键引脚模式
- TX(PA9) :复用推挽输出(GPIO_Mode_AF_PP)
- RX(PA10) :浮空输入 / 上拉输入(GPIO_Mode_IN_FLOATING)
4. 必记知识点
- 串口必须开启 GPIO 时钟
- 串口必须开启 USART 外设时钟
- 引脚必须配置为 复用功能
三、重映射(串口引脚重定义)
1. 什么是重映射
有些引脚不够用,可以把默认串口引脚 换到别的引脚,这就叫重映射。
2. USART1 重映射规则
- 默认:TX=PA9,RX=PA10
- 重映射:TX=PB6,RX=PB7
开启重映射需要:
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
3. 重映射作用
- 方便布线
- 解决引脚冲突
- 灵活分配硬件资源
4. 不重映射也能用
直接用默认引脚 PA9、PA10 最简单。
四、中断嵌套控制器 NVIC(串口中断)
1. 为什么要用串口中断
- 不用一直循环检测是否收到数据
- 收到数据自动进入中断
- 效率高、不占用 CPU
2. NVIC 在串口中断中的作用
- 开启串口接收中断
- 设置中断优先级
- 管理中断嵌套
3. 串口中断优先级规则
- 数字越小,优先级越高
- 抢占优先级 > 子优先级
- 一般设置:
- 抢占优先级:1
- 子优先级:1
4. 串口接收中断流程
电脑发数据 → RX 收到 → 触发中断 → 进入中断服务函数 → 读取数据
五、STM32 串口 USART 标准配置步骤
1、提前配置:中断分组(整个工程只配一次)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
第 1 步:开启 GPIO 时钟 + 串口时钟
RCC_APB2PeriphClockCmd(
RCC_APB2Periph_GPIOA |
RCC_APB2Periph_USART1,
ENABLE);
第 2 步:配置 TX(PA9)复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
第 3 步:配置 RX(PA10)浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
第 4 步:配置串口参数(波特率、数据位等)
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStruct);
第 5 步:开启接收中断(可选)
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
第 6 步:配置 NVIC 中断优先级
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
第 7 步:使能 USART 外设
USART_Cmd(USART1, ENABLE);
六、串口发送 / 接收标准函数模板
1. 发送一个字节
void Usart_SendByte(USART_TypeDef *USARTx, uint8_t byte)
{
USART_SendData(USARTx, byte);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
2. 发送字符串
void Usart_SendString(uint8_t *str)
{
while(*str)
{
Usart_SendByte(USART1, *str);
str++;
}
}
3. 串口接收中断服务函数
void USART1_IRQHandler(void)
{
uint8_t ch;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ch = USART_ReceiveData(USART1);
// 处理收到的数据
USART_SendData(USART1, ch); // 回显
}
}
七、浓缩口诀
开时钟 → 配 TX 复用 → 配 RX 输入 → 配置串口参数 → 开中断 → 配 NVIC → 使能串口
八、串口常用对应表
| 串口 | TX | RX | 中断函数 |
|---|---|---|---|
| USART1 | PA9 | PA10 | USART1_IRQHandler |
| USART2 | PA2 | PA3 | USART2_IRQHandler |
| USART3 | PB10 | PB11 | USART3_IRQHandler |
九、新手必避 4 个坑
- TX 必须是复用推挽输出
- RX 必须是浮空输入
- 波特率必须和串口助手一致
- 必须使能 USART 外设