1. 什么是UART?
(1)UART,通用异步收发器。相比于USART,UART收发是以字符为单位,没有CLK同步时钟。
(2)UART最主要的是三根数据线:
- TXD发送引脚
- RXD接收引脚
- GND接地引脚
(3)UART比较重要的几个参数:
- 波特率:每秒传送的码元数,比如9600,115200
- 数据位:典型值5、6、8、9位
- 奇偶校验位:一般分为奇校验和偶校验或者无校验位
- 停止位:典型值1、1.5、2位
(4)UART工作示意图如下图所示:

UART工作模式
2. 以STM32为例,分析UART工作原理
(1)了解STM32串口主要特性
- 可编程数据字长度(8位或9位)
- 可配置的停止位-支持1或2个停止位
- 可配置的使用DMA的多缓冲器通信
- 单独的发送器和接收器使能位
- 检测标志:接收缓冲器满、发送缓冲器空、传输结束标志
(2)了解STM32串口主要寄存器
STM32串口工作流程如下图:

UART工作流程
状态寄存器USART_SR
- TXE(发送数据寄存器空),当数据全部移入发送移位寄存器时,置1
- TC(发送完成),当一帧数据发送完成,且TXE=1时,置1
- RXNE(读数据寄存器非空),当有数据读入数据寄存器USART_DR时,置1
数据寄存器USART_DR,发送或接收缓存数据
控制寄存器1 USART_CR1,主要用于配置中断和使能
- UE(USART使能),TE(发送使能),RE(接收使能)
- TXEIE 发送缓冲区空中断使能
- TCIE 发送完成中断使能
- RXNEIE 接收缓冲区非空中断使能
(3)STM32基本配置:波特率,数据位,奇偶校验位,停止位,串口/中断使能
3. UART发送数据
(1)首先是对UART串口的初始化配置
(2)调用函数 HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
- huart是UART句柄结构体(姑且先这么叫)
- pData是待发送的字符数组,每次发送前都需要进行一次读写,可以调用sprintf函数
- Size是指pData字符数组的长度,一般是
strlen(pData)
- Timeout是待发送数据的生存时间,若超时会返回HAL_TIMEOUT
(3)自定义函数usUART_sendString(UART_HandleTypeDef *huart, uint8_t *pData)
- 这是根据HAL库发送函数由用户进行改写的字符串发送函数,形参只有两个
- 函数编写借鉴51单片机串口思想,代码编写如下:
void usUart_sendString(UART_HandleTypeDef *huart, uint8_t *pData)
{
/*
判断是否发送完成
*/
while(*pData)
{
/*
每次发送一个字符
*/
HAL_StatusTypeDef HAL_UART_Transmit(huart, pData, 1, 0xffff);
/*
字符指针移位,指向下一个字符的内容
*/
pData++;
}
}
4. UART接收
(1)首先对UART串口进行初始化
(2)调用函数HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
- 采用这种方法一般都是查询法,如下:
/* 判断是否接收成功 */
if(HAL_UART_Receive(&huart1, &uRx_Data, 1, 1000) == HAL_OK)
{
/* 将接收成功的数据通过串口发出来 */
HAL_UART_Transmit(&huart1, &uRx_Data, 1, 0xffff);
}
- 这种方式接收,必须事先知晓接收数据的长度,否则大概率会出错;其次,采用此方法会严重占用MCU的资源,尤其是放到while函数中。
- 除此之外,若是采用中断的方法,像这样接收后直接处理,就会出现另一个问题,即,还未发送出去(移位寄存器还未清空)就直接进入下一次中断
(3)采用中断方式(接收完成后再处理)
- 首先需要配置中断,中断配置在此略过……
- 中断函数代码编写如下:
/*
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
static unsigned char uRx_Data[1024] = {0} ; //存储数组
static unsigned char * pRx_Data = uRx_Data; //指向存储数组将要存储数据的位
static unsigned char uLength = 0 ; //接收数据长度
/* -1- 接收数据 */
HAL_UART_Receive(&huart1, pRx_Data, 1, 1000);
/* -2- 判断数据结尾 */
if(*pRx_Data == '\n')
{
/* -3- 将接收成功的数据通过串口发出去 */
HAL_UART_Transmit(&huart1, uRx_Data, uLength, 0xffff);
/* -4- 初始化指针和数据长度 */
pRx_Data = uRx_Data; //重新指向数组起始位置
uLength = 0; //长度清零
}
/* -5- 若未结束,指针往下一位移动,长度自增一 */
else
{
pRx_Data++;
uLength++;
}
HAL_UART_IRQHandler(&huart1);
}
参考资料
[2] UART串口通信协议原理