专栏:STM32F103入门教程
环境:Keil‑MDK + STM32F103 标准库3.5版本
一、串行通迅简介
USART 的全称是 Universal Synchronous/Asynchronous Receiver/Transmitter,中文译为通用同步/异步收发器。它是一种广泛用于微控制器(MCU)、微处理器和专用通信芯片中的硬件接口模块,专门负责设备之间的串行数据传输。

串行通信的基本特征是数据逐位顺序依次传输,一次传一个位(bit)。一个字节8个位,再加上起始,停止,校验后一个字节才能传输完成。发送方字节数据转换为串行比特流(一位一位地)发送出去;接收方接收数据时,又将接收到的串行比特流转换字节数据供 CPU 处理。
两大工作模式(USART 与 UART 的核心区别)
两大工作模式
USART 名称中的"Synchronous/Asynchronous"揭示了它最强大的特性------同时支持两种通信模式:
异步模式(UART 模式) :这是最常见的工作模式。发送端和接收端没有共享的时钟信号,双方依靠预先约定好的波特率(Baud Rate)以及起始位和停止位来同步数据。这种模式接线简单(通常只需 TX、RX 和 GND 三根线),应用极其广泛。
同步模式:发送端和接收端共享一条时钟信号线(CLK/SCLK)。数据在时钟信号的边沿进行严格同步。这种模式不需要起始位和停止位,数据传输速率更高且更可靠,但需要额外的时钟线,接线相对复杂。
主要技术特点
现代的 USART 模块通常具备高度灵活的配置能力,主要特点包括:
全双工操作:能够同时独立地进行数据的发送和接收。
可配置的帧格式:支持 5 到 9 位的数据位,以及 1 到 2 位的停止位。
硬件校验:由硬件自动支持奇偶校验位的发生与检验,减轻 CPU 负担。
高精度波特率发生器:独立生成通信所需的时钟频率,不占用系统的定时/计数器资源。
错误检测:硬件层面支持数据溢出、帧错误和校验错误的检测2。
典型应用场景
USART 是嵌入式系统中设备交互的桥梁,常见应用包括:
设备调试:作为嵌入式系统的控制台,将运行日志和变量状态通过串口打印到 PC 端。
模块通信:连接各类外部模块,如 GPS 模块、蓝牙模块、Wi-Fi 模块。
工业控制:结合 RS-232、RS-485 等物理层标准,用于 PLC 通信或传感器数据采集。
固件烧录:作为引导加载程序(Bootloader)的通道,用于给单片机下载和更新程序。
波特率 (Baud rate):
每秒传输的符号(信号变化)个数,UART 异步串口里,基本等价于每秒传输比特数 bit/s。单位:baud,常用:9600、115200。例:115200 = 每秒 115200 个比特。
UART 帧结构:起始位|数据位|停止位|校验位
UART 异步通信,没有时钟线,靠一帧的固定格式让接收方知道:数据什么时候来、什么时候结束。
标准格式:起始位 → 有效数据位 →(可选校验位)→ 停止位
1. 起始位 Start Bit
- 总线空闲时:TX 线保持高电平 (逻辑 1)
- 要发送数据时,先拉低电平,输出1 位的低电平(逻辑 0) ,这就是起始位。
- 作用:通知接收方:有数据来了,开始同步采样时钟。
- 接收端检测到电平从高→低的下降沿,就知道一帧开始,后续按照波特率的时间间隔采样后面电平。
关键点:空闲是高;起始位强制拉低。
2. 有效数据位 Data Bit
紧跟起始位之后,就是真正要传的数据。
- 长度可选:5、6、7、8 位 ;嵌入式几乎全部用 8 位(正好 1 字节)。
- 传输顺序:低位先行(LSB 先发送)
比如要发送 0x41(ASCII 'A',二进制
0100 0001),先发 bit0,最后发 bit7。
3. 校验位 Parity Bit(可选,可关闭)
紧跟数据位后面,用来做简单错误检测,不是纠错,只能发现出错 。
两种模式:
- 奇校验 ODD:数据位 + 校验位,整体 1 的总个数为奇数。
- 偶校验 EVEN:数据位 + 校验位,整体 1 的总个数为偶数。
- 无校验 NONE:不发送校验位,现在绝大多数设备用这个。
例子:数据 01000001,里面 1 的数量 = 2;
- 偶校验:校验位填 0,总和依旧偶数;
- 奇校验:校验位填 1,总和变成奇数。
缺点:只能检测部分错误;如果同时 2 位电平出错,校验检查不出来。现代嵌入式基本关闭校验,靠上层协议校验 CRC。
4. 停止位 Stop Bit
帧的结束标记,电平强制为高电平逻辑 1 。
可选长度:1 位、1.5 位、2 位。
- 绝大多数场景:1 停止位
- 老设备、长距离抗干扰:用 2 停止位。
二、STM32F103 串行设置相关
2.1 USART_InitTypeDef USART 初始化结构体
c
/**
* @brief USART Init Structure definition
*/
typedef struct
{
uint32_t USART_BaudRate; /*!< This member configures the USART communication baud rate.
The baud rate is computed using the following formula:
- IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))
- FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5 */
uint16_t USART_WordLength; /*!< Specifies the number of data bits transmitted or received in a frame.
This parameter can be a value of @ref USART_Word_Length */
uint16_t USART_StopBits; /*!< Specifies the number of stop bits transmitted.
This parameter can be a value of @ref USART_Stop_Bits */
uint16_t USART_Parity; /*!< Specifies the parity mode.
This parameter can be a value of @ref USART_Parity
@note When parity is enabled, the computed parity is inserted
at the MSB position of the transmitted data (9th bit when
the word length is set to 9 data bits; 8th bit when the
word length is set to 8 data bits). */
uint16_t USART_Mode; /*!< Specifies wether the Receive or Transmit mode is enabled or disabled.
This parameter can be a value of @ref USART_Mode */
uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
or disabled.
This parameter can be a value of @ref USART_Hardware_Flow_Control */
} USART_InitTypeDef;
- USART_BaudRate uint32_t
通信波特率,如 9600、115200。
注释里给出计算公式:硬件会根据 USART 外设时钟 PCLKx,自动计算整数分频 + 小分数分频,生成对应波特率。
✅我们只需要填数字,库函数内部自动算分频寄存器,不用自己算。 - USART_WordLength 数据位长度
可选宏:
USART_WordLength_8b:8 位数据位(最常用)
USART_WordLength_9b:9 位数据位(一般配合校验位使用)
对应帧结构里的有效数据位。
⚠️开启校验时:校验位会占用最高位;8 位数据模式下,校验位是第 8bit;9 位模式下校验位是第 9bit。 - USART_StopBits 停止位
可选宏:
USART_StopBits_1:1 位停止位(绝大多数项目)
USART_StopBits_0_5:0.5 停止位
USART_StopBits_2:2 位停止位(长距离通信)
USART_StopBits_1_5:1.5 停止位
对应帧结构的停止位。 - USART_Parity 校验位
可选宏:
USART_Parity_No:无校验 NONE(日常默认)
USART_Parity_Even:偶校验
USART_Parity_Odd:奇校验
对应帧结构的校验位。
注意:开启校验后,有效数据会少 1 位,校验位占用最高 bit。 - USART_Mode 收发模式
开启发送、接收,可以或运算同时打开:
USART_Mode_Tx:只开启发送
USART_Mode_Rx:只开启接收
USART_Mode_Tx | USART_Mode_Rx:收发都打开(最常用)
决定这个串口是发数据、收数据,还是收发都做。 - USART_HardwareFlowControl 硬件流控 RTS/CTS
硬件流控:额外两根引脚,防止接收方缓冲区满导致丢数据。
USART_HardwareFlowControl_None:不使用流控(99% 普通项目)
USART_HardwareFlowControl_RTS
USART_HardwareFlowControl_CTS
USART_HardwareFlowControl_RTS_CTS
普通 TTL 串口、USB‑TTL 调试不用流控,填 None。只有调制解调器、部分工业模块才用 RTS/CTS。
2.2 USART_SendData 串口发送
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);USARTx为要发送的串口,USARTx为要发送的数据。调用这个函数后,会把Data写到相应串口的数据寄存器准备发送,硬件外设自动完成。
2.3 USART_ReceiveData串口接收
uint16_t USART_ReceiveData(USART_TypeDef* USARTx); USARTx为要接收数据的串口,调用此函数可以得到数据寄存器的值。
细心的你会发现,串口发送和接收都是USARTx->DR,其实硬件内部接收和发送是两套数据寄存器,否则会冲突!
坑点:一定要判断标志位是否接收到数据,否则读到的是上一次的数据。
2.4 串行通迅中断标志位
中断宏 名称 触发条件 常用程度
USART_IT_CTS CTS 变化中断 硬件流控 CTS 引脚电平发生变化 很少用
USART_IT_LBD LIN 总线断开检测中断 检测到 LIN 总线 break 帧 极少,LIN 总线专用
USART_IT_TXE 发送数据寄存器空中断 发送数据寄存器 DR 为空,可以填入下一个待发字节 中等
USART_IT_TC 发送完成中断 一帧数据全部移位发送完毕(移位寄存器也空) 中等
USART_IT_RXNE 接收寄存器非空中断 收到 1 字节,DR 寄存器有新数据 ⭐非常常用
USART_IT_IDLE 总线空闲中断 检测到总线空闲(收到一帧之后,总线持续空闲) ⭐常用,不定长接收
USART_IT_ORE 溢出错误中断 旧字节还没读走,又收到新字节,数据丢失 错误中断
USART_IT_NE 噪声错误中断 采样收到噪声干扰 错误中断
USART_IT_FE 帧错误中断 没有检测到合法停止位(波特率错、干扰) 错误中断
USART_IT_PE 校验错误中断 开启校验位时,校验结果不匹配 错误中断
三、硬件接线
LED灯接线参考 零基础STM32入门教程--06-点灯实战
https://blog.csdn.net/oshan2012/article/details/163905246?spm=1011.2415.3001.5331
这里的串行通迅用的还是WCH-LINK,烧录和USB串口功能集成在一起。
STM32F103C8T6核心板---GND <----------> GND---WCH-LINK
STM32F103C8T6核心板---SWCLK <----------> SWCLK---WCH-LINK
STM32F103C8T6核心板---SWDIO <----------> SWDIO---WCH-LINK
STM32F103C8T6核心板---3V3 <----------> 3V3---WCH-LINK
STM32F103C8T6核心板---PA9 <----------> RX---WCH-LINK
STM32F103C8T6核心板---PA10 <----------> TX---WCH-LINK
串行通迅的发送和接收是交叉的,TX接对方的RX。
四、工程与程序编写
4.1工程复制
复制PWM工程文件夹为UART文件夹,PWM.uvprojx复制为UART.uvprojx。
4.2 添加驱动文件
- 直接在原usart.c文件里面添加,usart.h 不变。
c
#include "stdio.h"
#include "usart.h"
#include "led.h"
// MicroLIB 只需要实现这一个函数
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t)ch);
//等待发送寄存器为空,防止丢字符
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
void usart1_init(uint32_t baud)
{
GPIO_InitTypeDef gpio_init_struct; //GPIO端口设置
USART_InitTypeDef usart_init_structure;
NVIC_InitTypeDef nvic_init_structure; //嵌套向量中断控制器初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
gpio_init_struct.GPIO_Pin = GPIO_Pin_9; //PA.9
gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init_struct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &gpio_init_struct);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
gpio_init_struct.GPIO_Pin = GPIO_Pin_10;//PA10
gpio_init_struct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &gpio_init_struct);//初始化GPIOA.10
//USART 初始化设置
usart_init_structure.USART_BaudRate = baud; //串口波特率
usart_init_structure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
usart_init_structure.USART_StopBits = USART_StopBits_1; //一个停止位
usart_init_structure.USART_Parity = USART_Parity_No; //无奇偶校验位
usart_init_structure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
usart_init_structure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; //发送和接收模式
USART_Init(USART1, &usart_init_structure); //初始化串口1
//Usart1 NVIC 配置
nvic_init_structure.NVIC_IRQChannel = USART1_IRQn;
nvic_init_structure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级1
nvic_init_structure.NVIC_IRQChannelSubPriority =0; //子优先级0
nvic_init_structure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&nvic_init_structure); //根据指定的参数初始化VIC寄存器
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口1接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
uint8_t res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
res =USART_ReceiveData(USART1); //读取接收到的数据
if (res == 0xf0){
led_on();
}else if(res == 0xf1){
led_off();
}else{
printf(" error \r\n");
}
}
}
main.c
c
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include "exti.h"
#include "time.h"
#include <stdio.h>
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 设置中断优先级分组4
led_init();
delay_init();
usart1_init(115200);
//key_init();
//exit_init();
//time2_init(20000-1, 3600-1);
//time2_ch2_pwm_init(1000-1,72-1);
while(1)
{
}
}
4.3 编译烧录


串口助手设置的参数要和程序里面一致,选择串口要正确(插拨一下看电脑里面的设置管理器里面的端口)。
下发F0 LED点亮,下发 F1 LED熄灭,下发其它会打印error
发送数据格式要选择HEX,程序里面是接收一个字节判断的。
如果使用蓝牙模块用手机app或小程序控制的效果是一样的,如果要更多的控制和参数设置,通常要编写自定义的协议来实现。
资料下载
参考资料网盘 :
链接 : https://pan.baidu.com/s/1BrH0JbUY6z7j7gAihTa4Dw
提取码: qnws
本节课完整工程例程网盘:
链接: https://pan.baidu.com/s/1rl6EIqi9m6kQ1JFjuSqINg
提取码: bumc