STM32之串口详解
一、串口
1. 串口用途
串口是硬件中数据传递的一种方式,对应 USART 和 UART,包括 RS485、RS232、CAN 等。其主要用途是按照串行数据传递形式完成设备之间的数据通信。
2. 串行和并行
并行
- 连接方式:数据发送端和数据接收端通过 4 根数据线连接。
- 传输特点:数据线每次发送一个字节,数据可以同时发送 4 个字节数据。
串行
- 连接方式:仅有一个数据通道可以完成数据通信。
- 传输特点:数据字节按照顺序,类似于一串内容进行发送和接收。
接口类型
- 全双工:当前设备支持接收数据和发送数据,并且可以同时完成接收和发送。
- 半双工:当前设备支持接收数据和发送数据,但是设备如果处于接收数据状态,不允许发送数据;如果处于发送数据状态,不允许接收数据。
- 单工:设备区分发送端和接收端。
3. 同步和异步
同步
- 设备关系:主设备(Master)和从设备(Slave)。
- 时钟特点:数据发送和接收时钟总线步调一致。
- 传输细节:主设备和从设备之间进行数据传递,主设备数据位、字节和从设备数据位、字节时钟一致,即【同步状态】。
异步
- 设备关系:主设备(Master)和从设备(Slave)。
- 时钟特点:数据发送和接收时钟总线不同。
- 传输细节:主设备数据位、字节和从设备数据位、字节,直接的时间关系无所谓。例如主设备进行数据发送,从设备等了两年半之后接收数据(为【异步】情况)。且数据内容必须有明确的【起始标记】和【终止标记】。
4. USART 原理图
5. 分析 USART 相关技术
5.1 技术特征
5.2 关于发送数据和接收数据相关寄存器总线图
- MCU 通过串口 USART 进行数据发送,数据流转过程
- MCU -> PWDATA 数据总线 -> 写操作
- 发送数据寄存器 TDR ==> Transmit Data Register
- 硬件自动将 TDR 数据转移到发送移位寄存器,最后转出到 TX 接口
- MCU 通过串口 USART 进行数据接收,数据流转过程
- 数据通过外部的 RX 接口进入到接收移位寄存器
- 硬件自动接收移位寄存器中的数据,移动到接收数据寄存器 RDR ==> Read Data Register
- RDR 数据转到 PWDATA 数据总线 -> MCU
数据发送过程
- 首先,MCU 要发送数据时,会将数据通过 PWDATA 数据总线进行传输,随后执行写操作,把数据写入到发送数据寄存器(TDR,Transmit Data Register)中。
- 之后,硬件会自动把 TDR 里的数据转移到发送移位寄存器。发送移位寄存器的作用是将并行的数据逐位转换成串行数据。
- 最后,这些串行数据会从 TX 接口发送出去,完成数据的发送流程。
数据接收过程
- 当有数据要被 MCU 接收时,数据首先通过外部的 RX 接口进入到接收移位寄存器。接收移位寄存器会把接收到的串行数据逐位收集起来,转换成并行数据。
- 接着,硬件自动将接收移位寄存器中的数据移动到接收数据寄存器(RDR,Read Data Register)。
- 最后,RDR 中的数据会被转移到 PWDATA 数据总线,进而传输到 MCU 中,完成数据的接收过程。
5.3 NRZ 数据格式

5.4 USART 时钟分析和波特率计算
时钟使能
当前开发板操作的 USART1 --> 对应时钟是 RCC->APB2ENR 寄存器控制,位 14
串口数据发送和接收对应的【波特率 BRR】是根据对应时钟作为参考依据进行提供。
// 根据当前公式计算需要提供给寄存器的相关数据// 假设波特率是 115200 ==> USARTDIV 数据float usart_div = 72 * 1000 * 1000 / (16 * 115200);// usart_div == 39.0625/*将 usart_div 进行拆解,分别对应整数部分和小数部分内容,提供给当前 USART1 中用于计算波特率对应寄存器位。*/int usart_div_fraction = 0.0625 * 16 = 1 ==> 0x01int usart_div_Mantissa = 39 ==> 0x27// 两个数据进行组合 提供给 USART1 波特率寄存器的数据为USART_BRR = 0x271
5.5 USART 相关配置相关寄存器分析
5.5.1 控制寄存器1 (USART_CR1)
5.5.2 控制寄存器2 (USART_CR2)


5.5.3 波特比率寄存器 (USART_BRR)
控制波特率寄存器,对应之前计算的 BRR 寄存器所需数据内容。

5.6 USART1 时钟使能和 GPIO 配置
5.6.1 APB2 USRAT1 时钟配置
5.6.3 USART1 初始化配置代码实现
#include "usart1.h"void USART1_Init(u32 brr)
{/* 1. 时钟使能 GPIOA 和 USART1(均在 APB2 总线上)USART1 对应位 14,GPIOA 对应位 2 */RCC->APB2ENR |= (0x01 << 2) | (0x01 << 14);/* 2. 配置 PA9 (TX) 和 PA10 (RX) 的 GPIO 模式PA9 - 复用推挽输出模式(TX 发送端)PA10 - 浮空输入模式(RX 接收端) */GPIOA->CRH &= ~(0x00FF << 4); // 清除 PA9 和 PA10 原有配置GPIOA->CRH |= 0x0B << 4; // PA9 配置为复用推挽输出(50MHz)GPIOA->CRH |= 0x04 << 8; // PA10 配置为浮空输入/* 3. USART1 串口参数配置 */// 3.1 配置为 8 数据位、无校验位、1 停止位(8n1 格式)USART1->CR1 &= ~(0x01 << 12); // M 位清零,选择 8 数据位USART1->CR1 &= ~(0x01 << 10); // PCE 位清零,禁用校验位USART1->CR2 &= ~(0x03 << 12); // STOP 位清零,选择 1 停止位// 3.2 使能发送 (TE) 和接收 (RE) 功能USART1->CR1 |= (0x03 << 2); // TE 位(bit3)和 RE 位(bit2)置 1// 3.3 配置波特率(根据输入的 brr 参数计算)float usart_div = 72000000.0f / (16.0f * brr); // 72MHz 为 APB2 时钟频率u32 mantissa = (u32)usart_div; // 整数部分u32 fraction = (u32)((usart_div - mantissa) * 16); // 小数部分(4 位精度)USART1->BRR = (mantissa << 4) | fraction; // 写入波特率寄存器/* 4. 启动 USART1 */USART1->CR1 |= (0x01 << 13); // UE 位置 1,使能 USART1
}
5.7 MCU 通过 USART1 数据发送和数据读取
5.7.1 相关寄存器分析
数据寄存器 DR
USART_DR 寄存器数据发送和接收都是操作对应的 DR 寄存器
数据存入到 DR 寄存器中,对应【写操作】,数据会从 MCU TX 发送到其他设备
从 DR 寄存器中获取数据,对应【读操作】,数据会从外设的 TXD 进入到 MCU RX。
// 伪代码
USART1->DR = 1; // 【写操作】数据发送
val = USART1->DR; // 【读操作】数据接收
5.7.2 USART 发送一个字节数据到 PC
// 发送一个字节数据到 PC
void USART1_SendByte(u8 byte)
{/*利用 USART1_SR 寄存器,判断之前的数据内容是否发送完成,如果没有发送完成,本次发送操作进入【阻塞状态】如果 USART1_SR TC ==> 0 表示之前的数据发送未完成如果 USART1_SR TC ==> 1 表示之前的数据发送完毕TC Transmission Complete*/while (0 == (USART1->SR & (0x01 << 6))); // 等待发送完成(TC 位为 1)/*将需要发送的数据存储到 USART1->DR 数据寄存器中,DR 会将数据直接提供给 TDR 寄存器,TDR 寄存器会将数据提供给移位寄存器,SR 寄存器 TC 位在数据发送完毕后会置 1*/USART1->DR = byte;
}
5.7.3 PC 数据到 MCU 【待升级版】
// 从 PC 接收一个字节数据到 MCU
u8 USART1_ReceiveByte(void)
{u8 data = 0;/*判断在 USART1->SR 寄存器中,对应的 RXNE(Read data register not empty)标志位如果没有数据可以收到,RXNE 为 0如果有数据可以读取,RXNE 为 1while 进行 RXNE 标志位判断,如果没有数据当前循环【阻塞后续代码】*/while (0 == (USART1->SR & (0x01 << 5))); // 等待接收数据(RXNE 位为 1)data = (u8)USART1->DR; // 读取接收数据return data;
}
https://github.com/0voice