一、串口
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 ==> 0x01
int 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****初始化配置代码实现
cpp
#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。
cpp
// 伪代码
USART1->DR = 1; // 【写操作】数据发送
val = USART1->DR; // 【读操作】数据接收

5.7.2 USART发送一个字节数据到PC
cpp
// 发送一个字节数据到 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**【待升级版】**
cpp
// 从 PC 接收一个字节数据到 MCU
u8 USART1_ReceiveByte(void)
{
u8 data = 0;
/*
判断在 USART1->SR 寄存器中,对应的 RXNE(Read data register not empty)标志位
如果没有数据可以收到,RXNE 为 0
如果有数据可以读取,RXNE 为 1
while 进行 RXNE 标志位判断,如果没有数据当前循环【阻塞后续代码】
*/
while (0 == (USART1->SR & (0x01 << 5))); // 等待接收数据(RXNE 位为 1)
data = (u8)USART1->DR; // 读取接收数据
return data;
}