STM32串口详解
文章摘要: 串口(串行接口)是一种异步串行通信接口,通过单数据线逐位传输数据,实现设备互联。其核心特点是成本低、线路简洁、可靠性高,并解决了通信中的时序、同步和容错问题。相比并行通信,串口更适合远距离传输,采用全双工模式,支持多种校验方式。数据帧包含起始位、数据位(通常8位)、校验位和停止位。STM32单片机内置USART模块,可配置波特率、数据格式等参数,通过初始化流程实现串口通信功能。开发时需
一、什么是串口?

串行接口(Serial Port),简称串口,是一种异步串行通信接口。它通过单一数据线将数据逐位顺序传输,实现设备间的数据交换。
想象一下两个说不同语言的人如何交流?他们需要一个翻译官。在电子世界里,串口(Serial Port) 就扮演着这个角色!
通信协议的意义
如同两人通过声波交流需要遵循共同的语言规则一样,设备间通信也需要统一的"语言规则"——这就是通信协议。串口就是这种规范化的通信方式,解决了原始IO控制中时序、同步、容错等复杂问题。
解决三大头疼问题:
-
时序问题 → 约定好"说话节奏"(波特率)
-
同步问题 → 明确"谁先说谁后说"(通信协议)
-
容错问题 → 设置"你听清了吗"确认机制(校验位)
二、串口的作用与特点
主要作用:
-
设备互联:实现计算机与外部设备(如单片机、传感器、调制解调器等)的数据交换
-
远距离通信:支持较长距离的可靠数据传输
-
调试接口:广泛应用于嵌入式系统调试和程序烧录
核心特点:
-
✅ 成本低廉:硬件结构简单,实现成本低
-
✅ 线路简洁:最少只需3条线(发送、接收、地线)
-
✅ 易于使用:编程接口简单,开发便捷
-
✅ 可靠性高:具备完善的错误检测机制
三、通信方式对比
串行通信:精打细算的"单车快递"
定义:数据在单条数据线上按时间顺序逐位传输
通俗解释:数据像单车送货,一次只送一件,按顺序送达
特点:
-特点:
-
只需要一条"小路"(传输线少)
-
适合"长途运输"(长距离成本低)
-
可利用现有"公路网"(电话网络)
-
管理稍微复杂(控制逻辑相对复杂)
-
先送小件(低位LSB),后送大件(高位MSB)

并行通信:财大气粗的"货车车队"
定义:使用多条数据线同时传输多个数据位
通俗解释:数据像货车车队,多辆车同时出发送货
特点:
-
送货速度快
-
管理简单直接
-
"修路成本"高(线缆多)
-
“长途运输"容易"掉队”(同步困难)

对比总结
| 特性 | 串行通信 | 并行通信 |
|---|---|---|
| 传输线数量 | 少(1-3条) | 多(8条以上) |
| 传输速度 | 相对较慢 | 相对较快 |
| 成本 | 低 | 高 |
| 抗干扰能力 | 强 | 弱 |
| 适用距离 | 远距离 | 短距离 |
| 控制复杂度 | 较高 | 较低 |
现阶段绝大部分的通讯口都使用串口。
四、通信协议基础
通信模式
1.单工通信:广播电台
-
只能单向说话,不能接收
-
如:广播、电视信号

2.半双工通信:对讲机
-
可以说也可以听,但不能同时进行
-
需要说"完毕"才能切换

3.全双工通信:电话聊天
-
可以边说边听,双向同时进行
-
51单片机串口就是这种模式

五、关键技术参数
串口电平标准:音量的"大小"
-
TTL电平:+3V~+5V表示1,0V表示0(单片机常用)
-
RS232电平:-3-15V表示1,+3+15V表示0(计算机串口)
-
RS485电平:两线压差+2+6V表示1,-2-6V表示0(工业现场)
串口波特率:说话的"语速"
定义:单位时间内传输的二进制位数,决定通信速度
常见语速:
-
慢速:300、1200字/分钟
-
常速:2400、9600字/分钟
-
快速:19200、38400字/分钟
-
高速:115200、230400字/分钟
计算示例:
-
波特率9600:1秒传输9600位,每位时间=1/9600≈104.17μs
-
波特率115200:传输速度更快,每位时间约8.68μs

数据帧格式:说话的"语法规范"
一个完整的数据帧包含:
[起始位] + [数据位] + [校验位] + [停止位]
起始位:1位低电平,标志传输开始
数据位:5~9位实际数据,通常8位(1字节)
校验位:1位,用于错误检测
停止位:1~2位高电平,标志传输结束

校验方式详解
-
无校验(N):8位数据位,无校验位
-
奇校验(O):数据位中"1"的个数为奇数时,校验位为0,否则为1
-
偶校验(E): 数据位中"1"的个数为偶数时,校验位为0,否则为1
-
标记校验(M):校验位固定为1
-
空校验(S):校验位固定为0

空闲位
-
不属于数据帧部分
-
传输间隔期间总线保持高电平
-
标志当前无数据传输

LSB(Least Significant Bit)则是低地址存放最低有效字节
MSB(Most Significant Bit)是指低地址存放最高有效字节
案例:
例子1:通过串口发送十进制数字 27
二进制:00011011
传输顺序:起始位(0) + 11011000(数据位,LSB优先) + 校验位 + 停止位(1)
例子2:通过串口发送字符串 “hello”
ASCII编码:
h → 01101000 → 起始位 + 00010110 + 校验 + 停止位
e → 01100101 → 起始位 + 10100110 + 校验 + 停止位
l → 01101100 → 起始位 + 00110110 + 校验 + 停止位
l → 01101100 → 起始位 + 00110110 + 校验 + 停止位
o → 01101111 → 起始位 + 11110110 + 校验 + 停止位
六、开发工具准备
串口调试助手
功能:用于数据收发的调试和监控

USB转串口(TTL)模块:
功能:实现USB接口到TTL电平串口的转换

逻辑分析仪:
功能:用于信号波形的捕获和分析

七、STM32串口应用
硬件接线示意图

USART模块简介
STM32单片机内置多个USART(Universal Synchronous/Asynchronous Receiver/Transmitter)模块,支持全双工异步通信。

主要特性:
-
支持同步/异步通信
-
全双工操作
-
可配置数据位(8/9位)
-
可编程校验位
-
多个中断源
-
DMA支持

USART = 嘴巴 + 耳朵,既能说又能听!
USART模块的基本使用方法:
USART模块的初始化 → 配置波特率 + 数据帧格式
//USART1 波特率115200、8位数据位、无校验、1位停止位
#include "stm32f10x.h"
int main(void)
{
// 对 USART1 初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 开启时钟
// 配置相关参数 -> 配置波特率 + 数据帧格式
USART_InitTypeDef USART_InitStruct = {0};
USART_InitStruct.USART_BaudRate = 115200; // 波特率 115200
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Tx; // 全双工
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位 数据位
USART_InitStruct.USART_Parity = USART_Parity_No; // 无校验
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1位 停止位
USART_Init(USART1, &USART_InitStruct); // 完成初始化
// 使能 USART1
USART_Cmd(USART1, ENABLE);
}
这一步就相当于:
是否让嘴巴说、是否让耳朵听
让嘴巴怎么说、让耳朵怎么听
为串口初始化IO引脚
引脚配置问题
核心问题:
-
串口的引脚在哪里?
-
怎么设置引脚的参数(模式、速度)?

USART模块引脚分布及配置
1.引脚分布表

查看那些IO可以作为嘴巴或耳朵
2.IO配置表

赋予嘴巴、耳朵基本的说、听机能
3.引脚初始化代码
// 默认PA9、PA10引脚配置
void USART1_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置TX引脚(PA9)为复用推挽输出
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);
// 配置RX引脚(PA10)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// 重映射到PB6、PB7引脚配置
void USART1_GPIO_Remap_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 开启GPIOB和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 使能USART1重映射
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
// 配置TX引脚(PB6)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置RX引脚(PB7)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
使用串口发送数据
串口通信流程
空闲状态:
-
数据线保持高电平(逻辑1)
-
表示当前无数据传输
开始传输:
-
起始位:一个低电平脉冲(逻辑0)
-
标志数据传输开始
数据传输:
-
数据位按顺序逐位传输(先低位后高位)
-
数据位长度可配置(通常5-9位)
结束传输:
-
停止位:高电平脉冲(逻辑1)
-
标志本次传输结束
传输能力:
- 每次传输一个完整的字节(8位)或字符
数据发送过程
-
CPU将数据写入发送寄存器
-
USART将数据写入移位寄存器
-
通过移位寄存器将数据逐位发送
-
循环往复

关键标志位
标志位是什么?
我们可以通过这些标志位的值获取USART的工作状态
我们可以把它看作说话的进度条
TXE标志位:话准备好了吗?
-
Transmit Data Register Empty
-
判断发送数据寄存器是否为空
-
当TDR空时,TxE = 1(可以准备下一句话)
-
否则 TxE = 0(还在准备中)
TC标志位:话说完了吗?
-
Transmit Complete
-
判断数据是否发送完成
-
当TDR空且移位寄存器为空时,TC = 1(说完了)
-
否则 TC = 0(还在说)

编程接口
FlagStatus
作用:查询USART标志位的值,返回值:RESET-0;SET-1
// 查询USART标志位的值
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
// 返回值:RESET-0;SET-1
USART_SendData
作用:把要发送的数据写入到发送数据寄存器里
// 发送数据到数据寄存器
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
// 参数为uint16_t:为了支持9位数据位
实时发送函数
/**
* @brief 使用串口发送多个字节
*
* @param USARTx 指定发送串口
* @param pData 要发送的数据
* @param Size 要发送的字节的数
*/
void USART_Send_Bytes(USART_TypeDef* USARTx, uint8_t* pData, uint16_t Size)
{
for(uint32_t i = 0; i < Size; i++)
{
// #1. 等待发送数据寄存器空
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
// #2. 将要发送的数据写入到发送数据寄存器
USART_SendData(USARTx, pData[i]);
}
// #3. 等待数据发送完成
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
格式化打印字符串
格式化字符串的编程原理

1.生成格式化字符串

2.通过fputc发送到控制台

重写fputc函数
#include <stdio.h>
// 重定向printf到串口
int fputc(int ch, FILE *f)
{
// #3. 等待发送寄存器为空
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// #2. 发送字符
USART_SendData(USART1, (uint8_t)ch);
return ch;
}
// 重定向scanf从串口输入(可选)
int fgetc(FILE *f)
{
// #1. 等待接收到数据
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
// #2. 返回接收到的字符
return (int)USART_ReceiveData(USART1);
}
重写fputs函数其实就是给fputs函数输出重定向,使其发送目标由控制台变为串口
格式化输出时间字符串
#include <stdio.h>
#include <time.h>
// 格式化输出时间信息
void Print_TimeInfo(void)
{
// 获取系统运行时间(需要自己实现计时功能)
uint32_t system_time = GetSystemTick();
uint32_t hours = system_time / 3600000;
uint32_t minutes = (system_time % 3600000) / 60000;
uint32_t seconds = (system_time % 60000) / 1000;
uint32_t milliseconds = system_time % 1000;
printf("系统运行时间: %02lu:%02lu:%02lu.%03lu\r\n",
hours, minutes, seconds, milliseconds);
}
使用串口接收数据
数据接收过程
-
数据通过RX引脚逐位接收
-
数据存入接收移位寄存器
-
当完整字节接收完成后,数据转移到接收数据寄存器(RDR)
-
触发RXNE标志位
关键标志位
RxNE标志位:听到新内容了吗?
-
Receive Data Register Not Empty
-
判断接收数据寄存器是否为空
-
当RDR非空时,RxNE = 1(有新消息)
-
否则RxNE = 0(没听到什么)
可以把他当作听力的状态
编程接口
USART_ReceiveData
作用:从接收数据寄存器读取数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
// 作用:从接收数据寄存器读取数据
// 返回uint16_t:为了支持9位数据位
错误标志位说明
PE:Parity Error - 奇偶校验错
如果接收到的数据有校验错误,则PE = 1;否则 PE = 0
FE:Frame Error - 帧格式错误
接收到了无效的数据帧,则FE = 1;否则FE = 0
NE:Noise Error - 噪声错
接收的数据中检测到了噪声,则NE = 1;否则NE = 0
ORE:Overrun Error - 过载错
由于过载造成了数据丢失,则ORE = 1;否则ORE = 0
更多推荐



所有评论(0)