STM32 单片机USART 串口配置
目录
1、基础概念
1、什么是USART:USART 是单片机或嵌入式系统中非常常用的一个外设模块,全程是 Universal Synchronous/Asynchronous Receiver/Transmitter (通用同步/异步收发器).
USART 的主要功能是进行串行通信,也就是:
-
把并行数据(如 CPU 内部的数据)转换为串行数据发送出去;
-
把接收到的串行数据重新转换为并行数据给 CPU 使用。
串口:
- 单工:设备区分发送端和接收端,有且只允许从发送端到接收端进行数据传递
- 半双工 : 每一台设备即是发送端,也是接收端。但是在通信过程中,设备同一个周期内,仅 可以充当一个角色。例如 此时 A 设备发送数据到 B 设备,同一个时间周期内,不允许 B 发 送数据到 A
- 全双工 : 每一台设备即是发送端,也是接收端。同时在数据传递过程中,允许设备之间进通 信进行发送和接收操作。例如 A 设备发送数据到 B 设备,同时 B 设备可以发送数据到 A 设 备。 STM32 开发中,使用的串口 USART 通常情况下是全双工模式,利用两个数据线进行数据通信。
同步和异步:
- 同步:类似于无人机,仿生机械臂,同传技术。视觉同传,操作同传 数据同步传递,是发送端和接收端在同一个【时间参考】中,进行数据传递,因为不可 逾越的数据传递延迟问题,存在一定的数据丢失和期望数据同步滞后性。
- 异步:学习舞蹈,老师完成一个节拍动作,之后学员进行模仿。 数据异步传递,A 发送数据完成之后,B 进行数据解析,可以满足数据安全性和完整 性。
- 串口中使用的通常情况下都是异步传递方式,保证数据的完整性和安全性
2、原理图分析:

| 模块 | 功能 |
|---|---|
| USB Type-C 接口 | 连接电脑,通过 USB 传输信号 |
| CH340C 芯片 | 把 USB 信号转成串口信号 |
| TXD、RXD 引脚 | 串口通信线,接到单片机的 USART 引脚 |
| Q1、Q2 及 D2、R17、R21... | 用于自动控制单片机的 BOOT0 和 RESET,实现自动下载功能(类似 STM32 的 ISP 下载) |
数据传输流程:
从电脑发送给单片机:
电脑软件(串口助手 / ISP工具)
↓
USB 传输协议(复杂的包格式)
↓
CH340C 芯片
↓
转换为 UART 信号(TXD、RXD)
↓
单片机 USART 接收(RX 引脚)
单片机发送给电脑:
单片机 USART 发送(TX 引脚)
↓
CH340C 芯片(将 UART 转换为 USB)
↓
电脑 USB 驱动(虚拟 COM 口)
↓
电脑串口调试助手显示数据
以 USART1 为例,
USART1 的TX 和 RX 分别是 PA9 和 PA1。
3、USART技术分析:
在STM32文档中可以找到 USART 的章节。


框图重点:
- 发送器控制是通过寄存器 CR1 控制,涉及到的寄存位有 TE, PS,SBK
- 接收器控制是通过寄存器 CR1 控制,涉及到的寄存位有 RE, PCE,
- 数据寄存器 USARTx_DR 是根据 MCU 读操作和写操作行为区分数据走向
- 例如 USARTx_DR = 10; MCU 写操作,数据会存储到 TDR 寄存器中,USART 外设自行控 制,数据转移到【发送移位寄存器】,通过发送器进行数据发送
- u8 data = USARTx_DR; MCU 读操作,USART 外设会将存在 RDR 寄存器中的数据,提 供给 MCU 使用。数据是在 MCU 执行读操作之前,已经通过 RX 引脚读取到 DR 寄存器 内部,通过接收器控制完成。
- USART 中断控制对应寄存器为 CR1,涉及到中断标志控制为有 TXEIE TCIE RXNEIE
- IDLEIE。IE ==> Interrupt Enable
NRZ 数据格式:

波特率:
USART中断:
USART只有一个中断入口函数( USARTx_IRQHandler()),但有多个中断源可以触发,常用的USART中断标志位有RXNE(接收寄存器有新数据) 和 IDLE(接收线空闲——一帧结束)。
| 名称 | 场景 |
|---|---|
RXNE |
接收到数据(最常用) |
TXE |
发送下一个字节 |
TC |
一帧发送完毕 |
IDLE |
一帧接收完毕(空闲检测) |
在串口配置中通过 CR1 寄存器开启相应的中断使能,在中断处理函数中注意,要将 SR 寄存器的中断标志位清除,否则中断会反复进入。
不同中断有不同的清楚方式:
| 标志位 | 含义 | 何时产生 | 如何清除 | 是否必须清除 |
|---|---|---|---|---|
| RXNE | 接收寄存器非空 | 收到一个字节 | 读取 USART_DR 自动清除 | ✅ 必须 |
| TXE | 发送寄存器空 | 一个字节发送完成 | 写 USART_DR 自动清除 | ✅ 必须 |
| TC | 传输完成 | 最后一个字节发送完 | USART_ClearITPendingBit() 或读写寄存器 |
✅ 必须 |
| IDLE | 空闲帧检测 | 接收线空闲1帧时间 | 先读 SR,再读 DR 清除 | ✅ 必须 |
| ORE/FE/PE | 溢出/帧错误/校验错误 | 通信异常 | 读 SR 再读 DR 清除 | ✅ 必须 |
4、代码实现:
总体代码流程如下:
| 步骤 | 说明 |
|---|---|
| ① | 打开 GPIO 和 USART1 的时钟 |
| ② | 配置引脚复用功能(PA9 → TX, PA10 → RX) |
| ③ | 配置串口参数(波特率、数据位、校验、停止位等) |
| ④ | 使能 USART1 |
| ⑤ | 编写发送和接收函数(或使用中断/DMA) |
重点位第三步和第四步:
先看相关开发寄存器分析:
| 寄存器名 | 全称 | 作用 |
|---|---|---|
| USART_SR | 状态寄存器 (Status Register) | 标志发送、接收状态(如 TXE、RXNE 等) |
| USART_DR | 数据寄存器 (Data Register) | 发送或接收的数据存放处 |
| USART_BRR | 波特率寄存器 (Baud Rate Register) | 设置通信速率 |
| USART_CR1 | 控制寄存器1 (Control Register 1) | 开启 USART、配置数据位、使能发送接收 |
| USART_CR2 | 控制寄存器2 (Control Register 2) | 设置停止位、时钟相关设置 |
| USART_CR3 | 控制寄存器3 (Control Register 3) | 控制流、DMA、中断等扩展功能 |
重点位 CR1 和 CR2
CR1:

需要配置的有 USART使能,字长,校验控制使能,校验选择,发送使能,接收使能
具体需要看STM32 文档。
CR2:

配置停止位。
波特率寄存器:
此外还需要配置 DR(数据寄存器) 和SR(串口状态寄存器),具体看STM32 文档。
5、代码:单片机向电脑发送数据
STM32 单片机通过 USART1 的 TX 引脚,把数据发送到电脑
usart.h
#ifndef _USART_H
#define _USART_H
#include "stm32f10x.h"
//定义宏引脚
#define GPIOA_RCC_APB2_CLOCK_ENABLE (0x01 <<2) //开启时钟使能
#define USART1_RCC_APB2_CLOCK_ENABLE (0x01 << 14) //开启时钟使能
#define Floating_Input_Mode (0x04) //浮空输入模式
#define Alternate_Function_Push_Pull_Output_Mode (0x0B) //复用推挽输出
/**
USAR1 初始化函数,要求用户提供目标波特率数据常用波特率
4800 9600 115200
@param brr 用户提供的波特率参数
*/
void USART1_Init(u32 brr);
/**
发送数据 一个字节
*/
void USART1_SendByte(u8 byte);
/**
连续发送一段缓冲区数据
*/
void USART1_SendBuffer(u8 *buffer,u16 count);
/**
发送字符串
*/
void USART1_SendString(const char * str);
#endif
usart.c
#include "usart.h"
void USART1_Init(u32 brr)
{
// 1. 配置 USART1 对应引脚 GPIO 工作模式
RCC->APB2ENR |= GPIOA_RCC_APB2_CLOCK_ENABLE;
GPIOA->CRH &= ~(0x0F << 4);
GPIOA->CRH |= (Alternate_Function_Push_Pull_Output_Mode << 4);
GPIOA->CRH &= ~(0x0F << 8);
GPIOA->CRH |= (Floating_Input_Mode << 8);
// 2. 配置 USART1 串口相关功能
RCC->APB2ENR |= USART1_RCC_APB2_CLOCK_ENABLE;
USART1->CR1 |= (0x01 << 3); // USART1 CR1 TE 寄存器位开启
USART1->CR1 |= (0x01 << 2); // USART1 CR1 RE 寄存器位开启
USART1->CR1 &= ~(0x01 << 12);
USART1->CR1 &= ~(0x03 << 9);
// USART1 CR2 STOP 寄存器为明确为 0,1 个停止位。
USART1->CR2 &= ~(0x03 << 12);
// USART1 发送和接收 BRR 波特率配置。
// 利用参数波特率计算 USART_DIV 数据
float usart_div = 72000000 / (16 * brr);
// 计算器波特率寄存器整数位内容
u32 div_mantissa = (u32)usart_div;
// 计算器波特率寄存器小数位内容
u32 div_fraction = (u32)((usart_div - div_mantissa) * 16);
// 存储 div_mantissa 和 div_fraction 到 USART1 BRR 寄存器中
USART1->BRR |= (div_mantissa << 4) | div_fraction;
// USART1 UE 寄存器为配置,开启 USART1 工作
// 【注意】开启 USRAT1 需要在所有配置完成之后,如果先开启 USART1
// 相关配置无法完成。类似于 出发前准备好所有内容。
USART1->CR1 |= (0x01 << 13);
}
void USART1_SendByte(u8 byte)
{
while (0 == (USART1->SR & (0x01 << 6)));
USART1->DR = byte;
}
void USART1_SendBuffer(u8 *buffer, u16 count)
{
while (count--)
{
USART1_SendByte(*buffer);
buffer += 1;
}
}
void USART1_SendString(const char * str)
{
while (*str)
{
USART1_SendByte((u8)(*str));
str += 1;
}
}
mian.c
#include "usart.h"
int main(void)
{
USART1_Init(9600);
while (1)
{
USART1_SendString("Hello USART1\r\n");
Delay_ms(2000);
}
}
6、printf--串口重定向
先来说C语言中的printf底层逻辑,跳转printf查看定义可以看到如下:
#pragma __printf_args
extern _ARMABI int printf(const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1)));
这是因为因为printf 是 C 标准库函数,其具体实现代码已被编译成二进制库文件(如 .lib 或 .a),工程中只有头文件声明,没有源码。但是printf实际是调用底层的fputc函数一个字符一个字符发送。那么只需要对fputc重新定义即可调用printf实现串口发送。
///重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口DEBUG_USART */
USART_SendData(DEBUG_USART, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
return (ch);
}
更多推荐



所有评论(0)