USART
STM32提供了USART(Universal Synchronous Asynchronous Receiver and Transmitter)通用同步异步收发器。是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。UART相比USART去掉了同步通讯功能。
一共提供5个串口供开发者选择。
STM32的USART功能框图如下:
1.1 功能引脚说明
- TX:发送数据输出引脚;
- RX:接收数据输入引脚;
- SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚;
- nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能RTS流控制,当 USART接收器准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制; 可以把 nRTS 看作 接收端的“手”,举手(拉高)表示“先别发”
- nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能CTS流控制,发送器在发送下一帧数据之前会检测nCTS引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制;可以把 nCTS 看作 接收端给发送端的“绿灯”,低电平=绿灯,允许发;高电平=红灯,暂停;
- SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式;
1.2 STM32 串口寄存器配置开发流程(USART1)
1.2.1 使能时钟
需要使能两个时钟:GPIOA 时钟(因为 USART1 默认用 PA9=TX, PA10=RX)、 USART1 外设时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能 GPIOA 时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 使能 USART1 时钟
1.2.2 配置STM32的RXTX串口引脚
配置PA9 (TX) → 复用推挽输出、PA10 (RX) → 浮空输入
// PA9 = USART1_TX
GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9);
GPIOA->CRH |= (GPIO_CRH_MODE9_1 | GPIO_CRH_MODE9_0); // 输出模式 50MHz
GPIOA->CRH |= GPIO_CRH_CNF9_1; // 复用推挽输出
// PA10 = USART1_RX
GPIOA->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
GPIOA->CRH |= GPIO_CRH_CNF10_0; // 浮空输入
1.2.3 配置波特率
发送器和接收器的波特率是一致的,都是通过设置BRR寄存器来得到
波特率计算公式:
这里的是给外设的时钟(usart1在APB2上一般是72MHz,usart2,3,4,5在APB1上一般为36MHz)。
如下图BRR寄存器,往这个寄存器写USARTDIV即可完成设置波特率:
假设我们需要的波特率是115200,通过公式反推USARTDIV:72*10^6 / 16 / 115200
,(72*10^6为72Mhz
)则对应的分频值应该是:39.0625,把这个值写入到BRR寄存器中。BRR寄存器DIV_Fraction内容为小数部分 * 16
39.0625的小数部分:0.0625 * 16 = 1, 整数部分是:39(0x27 / 100111),所以写入到BRR寄存器的值是:0x0271 (1001110001);注: 16进制添加一位即二级制添加四位。
USART1->BRR = 0x0271; // 设置波特率 115200
1.2.4 配置 USART 控制寄存器
- UE (USART Enable) = 1 → 使能 USART
- TE (Transmitter Enable) = 1 → 使能发送器
- RE (Receiver Enable) = 1 → 使能接收器
- 8 数据位,1 停止位,无校验(默认)
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
- 寄存器
1.2.5 发送数据(轮询方式)
- 检查 TXE (发送数据寄存器为空时) 位 →
USART1->SR & USART_SR_TXE
- 写入
USART1->DR
/**
* @description: 发送一个字节
* @param {char} byte 要发送的字节
*/
void USART1_SendChar(char byte)
{
while(!(USART1->SR & USART_SR_TXE)); // 等待 SR寄存器下的TXE=1
USART1->DR = byte; // 写入数据寄存器
}
/**
* @description: 发送一个字符串
* @param {uint8_t} *str 要发送的字符串
* @param {uint16_t} len 字符串中字节的长度
* @return {*}
*/
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
Driver_USART1_SendChar(str[i]);
}
}
- 寄存器
1.2.6 接收数据(轮询方式)
- 检查 RXNE (接收数据寄存器非空) 位 →
USART1->SR & USART_SR_RXNE
- 读取
USART1->DR
char USART1_ReceiveChar(void)
{
while(!(USART1->SR & USART_SR_RXNE)); // 等待 RXNE=1
return USART1->DR; // 读数据寄存器
}
/**
* @description: 接收变长数据.接收到的数据存入到buff中
* @param {uint8_t} buff 存放接收到的数据
* @param {uint8_t} *len 存放收到的数据的字节的长度
*/
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
uint8_t i = 0;
while (1)
{
// 等待接收非空
while ((USART1->SR & USART_SR_RXNE) == 0)
{
//在等待期间, 判断是否收到空闲帧
if (USART1->SR & USART_SR_IDLE)
{
*len = i;
return;
}
}
buff[i] = USART1->DR;
i++;
}
}
- 寄存器
1.2.7 完整代码和测试
// Driver_USART
/**
* @description: 初始化串口1
*/
void Driver_USART1_Init(void)
{
/* 1. 开启时钟 */
/* 1.1 串口1外设的时钟 */
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
/* 1.2 GPIO时钟 */
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
/* 2. 配置GPIO引脚的工作模式 PA9=Tx(复用推挽 CNF=10 MODE=11) PA10=Rx(浮空输入 CNF=01 MODE=00)*/
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
GPIOA->CRH |= GPIO_CRH_MODE9;
GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
GPIOA->CRH &= ~GPIO_CRH_MODE10;
/* 3. 串口的参数配置 */
/* 3.1 配置波特率 115200 */
USART1->BRR = 0x271;
/* 3.2 串口使能,并使能接收和发送 */
USART1->CR1 |= USART_CR1_UE;
USART1->CR1 |= USART_CR1_TE;
USART1->CR1 |= USART_CR1_RE;
/* 3.3 配置一个字的长度 8位 */
USART1->CR1 &= ~USART_CR1_M;
/* 3.4 配置不需要校验位 */
USART1->CR1 &= ~USART_CR1_PCE;
/* 3.5 配置停止位的长度 */
USART1->CR2 &= ~USART_CR2_STOP;
}
/**
* @description: 发送一个字节
* @param {uint8_t} byte 要发送的字节
*/
void Driver_USART1_SendChar(uint8_t byte)
{
/* 1. 等待发送寄存器为空 */
while ((USART1->SR & USART_SR_TXE) == 0)
;
/* 2. 数据写出到数据寄存器 */
USART1->DR = byte;
}
/**
* @description: 发送一个字符串
* @param {uint8_t} *str 要发送的字符串
* @param {uint16_t} len 字符串中字节的长度
* @return {*}
*/
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
Driver_USART1_SendChar(str[i]);
}
}
/**
* @description: 接收一个字节的数据
* @return {*} 接收到的字节
*/
uint8_t Driver_USART1_ReceiveChar(void)
{
/* 等待数据寄存器非空 */
while ((USART1->SR & USART_SR_RXNE) == 0)
;
return USART1->DR;
}
/**
* @description: 接收变长数据.接收到的数据存入到buff中
* @param {uint8_t} buff 存放接收到的数据
* @param {uint8_t} *len 存放收到的数据的字节的长度
*/
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
uint8_t i = 0;
while (1)
{
// 等待接收非空
while ((USART1->SR & USART_SR_RXNE) == 0)
{
//在等待期间, 判断是否收到空闲帧
if (USART1->SR & USART_SR_IDLE)
{
*len = i;
return;
}
}
buff[i] = USART1->DR;
i++;
}
}
// main.c
#include "string.h"
uint8_t buff[100] = {0};
uint8_t len = 0;
int main()
{
Driver_USART1_Init();
while (1)
{
Driver_USART1_ReceiveString(buff, &len);
Driver_USART1_SendString(buff, len);
}
}
1.2.8 中断方式接收数据
USART提供了多个中断事件
- 使能串口、使能接收中断 RXNEIE
- 配置 NVIC
// 上节初始化代码Driver_USART1_Init加上下面代码:
USART1->CR1 |= USART_CR1_IDLEIE; /* 空闲中断 */
USART1->CR1 |= USART_CR1_RXNEIE; // 使能接收中断
NVIC_SetPriorityGrouping(3); /* 配置优先级组 */
NVIC_SetPriority(USART1_IRQn, 2);/* 设置优先级 */
NVIC_EnableIRQ(USART1_IRQn); // 开启 NVIC 中断
USART1->CR1 |= USART_CR1_UE;/* 使能串口 */
// 这里只是声明,不分配存储空间,分配在main.c中,最终编译时,这几个变量只会有一份存储空间
extern uint8_t buffer[100];
extern uint8_t size;
extern uint8_t isOver;
// 中断服务函数
void USART1_IRQHandler(void)
{
if(USART1->SR & USART_SR_RXNE) { // 收到数据,SR寄存器的USART_SR_RXNE这位为高电平
buffer[size] = USART1->DR;// 读数据放到缓冲区
size++;
}else if (USART1->SR & USART_SR_IDLE)
{
// 字符串整体接收完成
// 清除IDLE标志位
USART1->DR;
isOver = 1;
}
}
main.c
#include "stm32f10x.h"
#include "Driver_USART.h"
#include "string.h"
// 定义全局变量,接收缓冲区和size
uint8_t buffer[100] = {0};
uint8_t size = 0;
// 定义标志位
uint8_t isOver = 0;
int main(void)
{
Driver_USART1_Init();
// 发送字符串
uint8_t * str = "Hello, World!\n";
Driver_USART1_SendString(str, strlen((char *)str));
while(1)
{
// 中断接收到数据,回复相同数据
if (isOver)
{
Driver_USART1_SendString(buffer, size);
// 清除标志
isOver = 0;
size = 0;
}
}
}
1 游客 2025-09-01 11:26 回复
1
1 游客 2025-09-01 12:02 回复
1
@@ZZAh2 游客 2025-09-01 12:03 回复
1
1 游客 2025-09-01 12:06 回复
1
1 游客 2025-09-01 12:07 回复
1