文章目录
单片机的U(S)ART
- 以stm32u575为例
逻辑框图

不同UART接口的类型

不同U(S)ART接口类型的主要属性

U(S)ART的使用
- 以stm32u575为例
发送数据
寄存器配置过程
- 在USART_CR1寄存器中编程M位以定义字长,字长默认是8bit,但可以选 7, 8, 9bit
- 使用USART_BRR寄存器选择所需的波特率,常见的有 9600,19200,38400,57600,115200,230400
- 在USART_CR2寄存器中编程停止位的数量,停止位,默认是1bit,但可以选 1.5bit, 2 bit
- 通过将USART_CR1寄存器中的UE位写1来使能USART
- 如果需要进行多缓冲通信,则在USART_CR3寄存器中选择DMA使能(DMAT)。并按照"使用USART和DMA的连续通信"中的说明配置DMA寄存器
DMA,即 Direct Memory Access,可以在无需处理器干涉的情况下,直接完成数据在存储器和外设之间的传递,例如发送数据,可以直接从内存写入串口的发送数据寄存器,直到需要发送的数据都通过该寄存器发送完成,才触发中断
发送数据过程
- 设置USART_CR1中的TE位以发送空闲帧作为首次传输(可选步骤)
此处,"帧"的概念为从起始位到停止位的一串数据流
- 将要发送的数据写入USART_TDR寄存器。如果是单缓冲模式,则对每个待发送数据重复此操作
- 当FIFO模式禁用时,向USART_TDR写入数据会清除TXE标志。
- 当FIFO模式启用时,向USART_TDR写入数据会将一个数据添加到TXFIFO中。写入USART_TDR的操作需在TXFNF标志置位时进行,该标志会保持置位直到TXFIFO满。
- 当最后一个数据写入USART_TDR寄存器后,等待TC=1
- 当FIFO模式禁用时,表示最后一帧传输完成
- 当FIFO模式启用时,表示TXFIFO和移位寄存器均为空
- 此检查是为了避免在USART禁用或进入停机模式时损坏最后一次传输
发送相关中断
| 中断事件 | 中断标志 | 中断使能位 | 中断清除方法 |
|---|---|---|---|
| TDR 空 | TXE | TXEIE | 写 TDR |
| TXFIFO 不满 | TXFNF | TXFNFIE | 写满 TXFIFO |
| TXFIFO 空 | TXFE | TXFEIE | 写 TDR 或向 TXFRQ 位写 1 |
| 达到 TXFIFO 阈值 | TXFT | TXFTIE | 写 TDR |
发送两种模式
阻塞(轮询模式)
- 阻塞:指处理器一直等待该过程处理完成才转入下一个流程,如字符串 "Hello World" 的发送

c
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t
*pData, uint16_t Size, uint32_t Timeout)
非阻塞(中断模式)
- 中断是处理器非阻塞模式之一(另一种是DMA+中断方式),指处理器启动过程后,不必一直等待该过程处理完毕,而是可以通过完成的中断来获取完成的消息。启动处理过程后,可以转向处理其他的流程

c
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t
*pData, uint16_t Size);
//回调函数
//所有数据发送完成
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
//发送缓冲区空(TXFE引发)
void HAL_UARTEx_TxFifoEmptyCallback(UART_HandleTypeDef *huart);
HAL_UART_Transmit_IT() 调用一次后,发送完成,即取消发送中断状态!需要手动再次调用HAL_UART_Transmit_IT()
FIFO的使用
- FIFO是部分单片机每个串口硬件特有的缓冲区


接收数据
起始位和数据位的检测
起始位

数据位

寄存器配置过程
- 配置USART_CR1寄存器的M位以定义字长(数据位长度)。
- 通过波特率寄存器USART_BRR设置所需的通信波特率。
- 在USART_CR2寄存器中编程设定停止位的数量。
- 将USART_CR1寄存器的UE位置1以启用USART模块。
- 若需多缓冲区通信,则需在USART_CR3寄存器中使能DMA(设置DMAR位),并按照"使用USART和DMA实现连续通信"的内容说明配置DMA寄存器。
- 将USART_CR1寄存器的RE位置1,使能接收器开始搜索起始位
接收数据过程
- FIFO模式禁用时:RXNE位会被置1,表示移位寄存器的内容已传输至RDR(接收数据寄存器),即数据已接收完成并可读取(同时包含相关错误标志)。
- FIFO模式启用时:RXFNE位会被置1,表示接收FIFO非空。读取USART_RDR将返回FIFO中最先存入的数据。接收的数据会与对应的错误位一同存入RXFIFO。
- 若RXNEIE位(FIFO模式启用时为RXFNEIE位)被置1,则会产生中断。
- 若检测到帧错误、噪声、奇偶校验错误或接收溢出错误,则会置位相应的错误标志位。四个错误检测标志:
- 溢出错误:之前的数据未处理,新收到了数据
- 帧错误:未在约定的时间范围内检测到结束位
- 奇偶校验错误:校验位不符合约定的奇偶校验规则
- 噪声检测:过采样过程,中心点采样,3次采样,结果不一致
接收相关中断
| 中断事件 | 中断标志 | 中断使能位 | 中断清除方法 |
|---|---|---|---|
| RDR 不为空 | RXNE | RXNEIE | 读 RDR 或向 RXFRQ 写入 1 |
| RXFIFO 不为空 | RXFNE | RXFNEIE | 读 RDR 直到 RXFIFO 空或向 RXFRQ 写入 1 |
| RXFIFO 满 | RXFF | RXFFIE | 读 RDR |
| 达到 RXFIFO 阈值 | RXFT | RXFTIE | 读 RDR |
| 溢出错误 | ORE | RXNEIE/RXFNEIE | 向 ORECF 写 1 |
| 检测到数据线空闲 | IDLE | IDLEIE | 向 IDLECF 写 1 |
| 奇偶校验错误 | PE | PEIE | 向 PECF 写 1 |
接收两种模式
阻塞(轮询)模式

c
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData,
uint16_t Size, uint32_t Timeout);
非阻塞(中断)模式

c
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData,
uint16_t Size);
//回调函数
//接收数据长度达到预设值引起的
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
//RXFF引起
void HAL_UARTEx_RxFifoFullCallback(UART_HandleTypeDef *huart);
注意: HAL_UART_Receive_IT() 每调用一次,接收到要求长度的数据后,即取消接收中断状态,不再接收新的数据!需要重新手动开启接收中断
接收不定长数据
- 接收过程配合使用空闲(IDLE)中断

c
HAL_StatusTypeDef HAL_UARTEx_ReceiveTodle_IT(UART_HandleTypeDef *huart, uint8_t
*pData, uint16_t Size);
//回调函数
//IDLE引起
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);
注意: HAL_UARTEx_ReceiveToIdle_IT() 每调用一次,接收时检测到空闲中断或者接收到要求长度的 数据后,即取消接收所有中断状态,不再接收新的数据!
print函数发送实现原理
- printf()函数是通过调用库中的字符输出函数(不同的编译器,使用的库函数略有差异)实现数据的输出,字符输出函数默认是把字符输出到调试器控制窗口,要把数据通过USART输出到串口,需对字符输出函数重定向到USART端口上去
方式一:使用MicroLib库
- 在Keil MDK的STM32工程中,点击魔术棒,打开工程配置界面

MicroLib是一个专为深度嵌入式系统设计的C库,优化了代码和数据内存使用,适合在没有操作系统的环境中运行。 相较于标准C库,MicroLib不包含文件I/O和宽字符支持,且某些函数执行速度可能较慢
- fputc() 函数重定向
c
#include <stdio.h>
int fputc(int ch, FILE *f)
{
//向调试串口发送1字节数据
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
return ch;
}
方式二:取消半主机模式
- 半主机模式是嵌入式开发中一种特殊的调试机制,允许运行在目标设备上的代码使用主机(通常是开发计算机)的资源进行输入/输出操作。具体来说半主机模式通过以下方式工作:
- 嵌入式代码执行特定的指令序列(通常是断点指令或软中断)
- 调试器捕获这些指令并解释为服务请求
- 主机提供请求的服务(如文件I/O、控制台输出等)
- 结果通过调试接口返回给目标设备
c
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
// ARM Compiler 6 (MDK V6)
// 禁用半主机模式
__asm(".global __use_no_semihosting\n");
//标准库需要的支持函数
struct FILE
{
int handle;
};
FILE __stdout;
void _ttywrch(int ch)
{
ch = ch;
}
#else
// ARM Compiler 5 (MDK V5)
// 禁用半主机模式
#pragma import(__use_no_semihosting)
struct __FILE {
int handle; // 占位符,无实际用途(可简化)
};
FILE __stdout, __stdin; // 标准输入/输出流
#endif
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
// 重定向 fputc 函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 100);
return ch;
}