本节将进行实战,基础了解请查看第二节(Whappy)
开始背!! USART ---》全双工 异步/同步 点对点
USART:STM32自带硬件电路,通过配置相对应的寄存器来设置数据帧的发送,我们收发只需要一个简单的函数,方便我们调用即可!
经过本人总结,完全可以把TX等价为人的嘴巴,RX等价人的耳朵,大脑就相当于人的存储器。这个世界是抽象的,人为定义的,可解和无解的
通过 UART 的基本结构可以看出,系统的主频通过分频器将时钟信号分频为双方约定的波特率,以确保数据传输的同步性。
在数据发送时,首先将数据写入发送数据寄存器(TXD寄存器)进行缓存,接着通过移位寄存器将数据逐位移送,并最终传输到 GPIO 的 TX 引脚进行发送。数据发送的过程涉及两个步骤:先将数据缓冲,再通过移位寄存器逐位传输。
同样,在数据接收时,接收到的信号首先通过接收的移位寄存器进行缓冲,再送到接收数据寄存器中。移位寄存器在接收过程中起到了逐位接收数据的作用,确保数据能够正确地存入接收寄存器中。
简而言之,TX 和 RX 的数据发送和接收都要经过移位寄存器的处理,这样的结构保证了串行数据能够按位正确传输和接收。
细节问题!!
从图中可以看出,UART 通信支持不同的字长设置,包括 9 位字长和 8 位字长,并且可以选择是否使用校验位。
需要注意的是,当使用 8 位字长并启用校验时,由于校验位的存在,数据位将减少一位,这样就无法构成一个完整的字节。因此,在选择字长时,若使用 9 位字长,可以选择启用校验位,而 8 位字长通常应选择不使用校验位,以确保每个数据帧完整且符合字节的要求。
USART 的停止位在串行通信中起到了非常重要的作用,它用于标识一个数据帧的结束。具体来说,停止位用于确保数据接收端能够准确地判断每个数据字节的边界,从而区分相邻的数据帧。下面是停止位的作用详细解释:
1. 数据帧的结束标志
- 停止位用于标识数据帧的结束。一个数据帧通常由 起始位 (Start Bit)、数据位 (Data Bits)、校验位 (Parity Bit)和 停止位(Stop Bit)组成。当接收端接收到停止位时,它就知道一个数据帧已经接收完毕,可以开始处理数据或等待下一个数据帧的到来。
2. 提供接收端的时间间隔
- 停止位为接收端提供了一个时间间隔,使接收端有足够的时间去处理接收到的数据并准备好接收下一个数据帧。这有助于防止接收端因为过快接收数据而出现数据丢失或错位。
- 例如,假设设置了一个 1 位停止位,接收端会在数据帧的每个字节结束后等待 1 个位时间。若设置了 2 位停止位,则会等待 2 个位时间。这个间隔为接收端提供了一个缓冲期。
3. 保证数据同步
- 在串行通信中,停止位确保了数据帧的同步性。停止位的长度通常为 1 位或 2 位。当接收端检测到停止位时,它会确定接收到的数据的边界,并与其他数据帧区分开来。如果没有停止位,接收端就无法知道何时停止接收,可能会导致接收错误。
4. 防止数据重叠
- 停止位防止了数据帧之间的重叠,特别是在高速传输中,它确保每个数据字节的传输是清晰的、独立的。若没有停止位,接收端可能无法准确分辨每个数据字节的起始和结束,导致数据错误。
5. 灵活配置停止位
- 大多数 USART 实现允许配置 1 位停止位 或 2 位停止位。选择更长的停止位可以在某些噪声较大的环境中提高可靠性,避免数据接收错误,但也会稍微降低通信速率。
总结:
停止位的作用可以概括为:
- 标识数据帧的结束。
- 提供接收端缓冲时间,确保数据接收不重叠。
- 保证数据同步,避免接收错误。
- 提高通信的稳定性,特别是在高速传输时。
合理设置停止位数量,可以有效提升通信的稳定性与可靠性,尤其在长时间或高速的串行数据传输中至关重要。
一般我们,就选择一位停止位。
这张图片展示了 STM32 微控制器中 USART (通用同步异步收发器) 的波特率寄存器 (USART_BRR) 结构。波特率寄存器用于配置 USART 接口的数据传输速度。以下是对其结构和配置方法的详细解释:
1. 波特率计算公式
公式:波特率 = \frac{{f_{PCLK2/1}}}{{16 \times \text{DIV}}
- 其中,fPCLK2/1f_{PCLK2/1}fPCLK2/1 是 USART 使用的外部时钟频率(系统时钟经过分频后的频率)。
- DIV 是设置在 USART_BRR 寄存器中的分频系数。
- 使用此公式计算的波特率决定了 USART 接口传输数据的速度。
2. 波特率寄存器 (USART_BRR)
USART_BRR 寄存器用于设定 DIV 值,该寄存器中包括两个部分:
- DIV_Mantissa[11:0]:分频器的整数部分,占用寄存器的第 15:4 位。
- DIV_Fraction[3:0]:分频器的小数部分,占用寄存器的第 3:0 位。
这些位的组合形成了一个完整的分频系数(DIV),从而可以微调波特率。
3. 寄存器各部分说明
- 位 31:16:保留位,硬件默认为 0。
- 位 15:4 :DIV_Mantissa[11:0],是波特率分频器的整数部分,用于主要的分频比设定。
- 位 3:0 :DIV_Fraction[3:0],是波特率分频器的小数部分,用于更精细的波特率调整。
4. 设置波特率的步骤
- 确定系统时钟频率 fPCLK2/1f_{PCLK2/1}fPCLK2/1。
- 根据期望的波特率,计算分频系数 DIV\text{DIV}DIV。 DIV=fPCLK2/116×波特率\text{DIV} = \frac{{f_{PCLK2/1}}}{{16 \times \text{波特率}}}DIV=16×波特率fPCLK2/1
- 将计算得到的 DIV 分成整数和小数两部分,分别写入 DIV_Mantissa 和 DIV_Fraction。
- 将 USART_BRR 寄存器配置为这些值后,USART 即会按照配置的波特率进行数据传输。
例子
其实,我们不必大费周章,STM32已经帮我们写好了,库函数,直接输入库函数即可
确实,STM32 提供了丰富的标准库函数和 HAL 库函数,我们可以直接调用库函数来配置 USART 的波特率,而不必手动计算和写入寄存器。通过库函数,可以简化代码的编写,提高代码的可读性和可维护性。
下面是使用 HAL 库的一个示例代码来设置 USART 波特率:
#include "stm32f1xx_hal.h" // 根据芯片型号包含合适的 HAL 头文件
UART_HandleTypeDef huart1; // 声明一个 UART 句柄
void USART1_UART_Init(void)
{
huart1.Instance = USART1; // 选择 USART1
huart1.Init.BaudRate = 9600; // 设置波特率,例如 9600
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位长度,8位
huart1.Init.StopBits = UART_STOPBITS_1; // 设置停止位,1位停止位
huart1.Init.Parity = UART_PARITY_NONE; // 设置校验位,无校验
huart1.Init.Mode = UART_MODE_TX_RX; // 设置模式,收发模式
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控
huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16倍过采样
if (HAL_UART_Init(&huart1) != HAL_OK) // 调用初始化函数
{
// 错误处理
Error_Handler();
}
}
解释
-
UART_HandleTypeDef :这是一个 UART 句柄结构体,用于配置和操作 USART。在这里我们定义了
huart1
,并将其关联到USART1
外设。 -
huart1.Instance :指定要配置的 USART 外设。在这里选择
USART1
。 -
huart1.Init.BaudRate:设置波特率。这里我们设置为 9600,可以根据需要修改为其他波特率。
-
huart1.Init.WordLength :设置数据位长度。
UART_WORDLENGTH_8B
表示 8 位数据位。 -
huart1.Init.StopBits:设置停止位数量,这里选择 1 位。
-
huart1.Init.Parity :设置校验方式。
UART_PARITY_NONE
表示无校验位。 -
huart1.Init.Mode :设置 UART 工作模式,这里选择收发模式(
UART_MODE_TX_RX
),即既可以发送也可以接收数据。 -
huart1.Init.HwFlowCtl :硬件流控,设置为无流控(
UART_HWCONTROL_NONE
)。 -
huart1.Init.OverSampling :选择过采样倍数,
UART_OVERSAMPLING_16
为 16 倍过采样。通常选择 16 倍过采样来保证通信质量。 -
HAL_UART_Init :调用 HAL 库的 UART 初始化函数,传入配置好的
huart1
句柄,即可完成 USART 配置。此函数会自动计算波特率分频值并写入相应的寄存器,因此无需手动计算。
使用 HAL 库的好处
STM32 的 HAL 库封装了底层寄存器的操作,使得我们只需配置参数即可完成初始化。这样不仅简化了代码量,也提升了代码的通用性和可读性。如果后续需要调整波特率等参数,只需修改相关配置即可,无需手动计算和操作寄存器。
STM32 标准库和 HAL 库已经封装好了 USART 波特率的设置功能,我们可以直接使用库函数来配置 USART 波特率,而不需要手动计算并设置寄存器的值。
使用 STM32 库函数配置 USART 波特率
在 STM32 的 HAL 库中,HAL_UART_Init()
函数可以用来初始化 USART 并设置波特率。我们只需要定义一个 UART_HandleTypeDef
结构体,并设置其波特率等参数,然后调用这个初始化函数即可。
代码示例
假设我们使用 USART1
,波特率为 9600:
#include "stm32f1xx_hal.h"
UART_HandleTypeDef huart1;
void USART1_UART_Init(void) {
huart1.Instance = USART1; // 指定使用 USART1
huart1.Init.BaudRate = 9600; // 设置波特率为 9600
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据长度为8位
huart1.Init.StopBits = UART_STOPBITS_1; // 设置停止位为1位
huart1.Init.Parity = UART_PARITY_NONE; // 设置无校验位
huart1.Init.Mode = UART_MODE_TX_RX; // 设置模式为收发模式
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 设置硬件流控制为无
huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 设置过采样为16
if (HAL_UART_Init(&huart1) != HAL_OK) {
// 初始化错误处理
Error_Handler();
}
}
-
UART_HandleTypeDef 结构体 :
huart1
是一个 UART 的句柄,用于配置和控制 USART1。 -
huart1.Instance = USART1 :指定我们要初始化的 UART 外设为
USART1
。 -
huart1.Init.BaudRate = 9600 :设置 UART 的波特率为 9600。库函数会自动计算适合的
USART_BRR
寄存器值来实现这个波特率。 -
其他配置:
WordLength
设置为 8 位数据长度。StopBits
设置为 1 位停止位。Parity
设置为无校验位。Mode
设置为收发模式(即使能发送也能接收)。HwFlowCtl
设置为无硬件流控制。OverSampling
设置为 16 倍过采样。
-
HAL_UART_Init(&huart1) :调用 HAL 库函数进行初始化。这个函数会根据
huart1
的配置,自动设置 USART 的相关寄存器(包括波特率寄存器USART_BRR
)。
优点
使用库函数进行 UART 配置的优点在于:
- 简化了配置步骤,无需手动计算和设置寄存器。
- 提供更高的可移植性和可维护性,因为 HAL 库在不同系列的 STM32 芯片中都有类似的接口。
- 减少了出错的可能性。
STM32 的标准库(STM32 Standard Peripheral Library,简称 StdPeriphLib)是 STMicroelectronics 为 STM32 微控制器提供的早期外设库,主要用于简化外设的配置和使用。相比于低层直接操作寄存器,标准库通过封装函数接口,使代码更易读易维护。虽然标准库在最新的 STM32 开发中被 HAL 库替代,但仍然被许多开发者使用,特别是在老旧的 STM32 芯片上。
使用 STM32 标准库初始化 USART 波特率的示例代码
以 STM32F103 微控制器和 USART1 为例,设置波特率为 9600。这个库不包含 HAL 库那样的结构体 UART_HandleTypeDef
,而是直接通过库函数操作外设寄存器。
代码示例
#include "stm32f10x.h"
void USART1_Init(void) {
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 1. 开启 GPIOA 和 USART1 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 2. 配置 PA9 为 USART1_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX 引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚速率
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 设置为复用推挽模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置 PA10 为 USART1_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX 引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置 USART 参数
USART_InitStructure.USART_BaudRate = 9600; // 波特率 9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 1 位
USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 使能接收和发送
// 4. 初始化 USART1
USART_Init(USART1, &USART_InitStructure);
// 5. 启动 USART1
USART_Cmd(USART1, ENABLE);
}