目录
  1. 1. UART——串口详解
    1. 1.1. 串口通信原理
    2. 1.2. UART串口波特率计算
    3. 1.3. 串口发送配置流程
    4. 1.4. 串口接收流程
    5. 1.5. 串口接受发送实验
UART——串口详解

UART——串口详解

沉迷仁王无法自拔>.<

串口通信原理

image-20191107220935786

单工:数据传输只支持数据在一个方向上传输

半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;

全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。

同步通信:带时钟同步信号传输。SPI,IIC通信接口。

异步通信:不带时钟同步信号。UART(通用异步收发器),单总线。

常见的串行通信接口

通信标准 引脚说明 通信方式 通信方向
UART (通用异步收发器) TXD:发送端 RXD:接受端 GND:公共地 异步通信 全双工
单总线 (1-wire) DQ:发送/接受端 异步通信 半双工
SPI SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入 同步通信 全双工
I2C SCL:同步时钟 SDA:数据输入/输出端 同步通信 半双工

异步通信UART包含三点知识:

① 物理层(电气层:接口决定):通信接口(RS232,RS485,RS422,TTL)

② 数据格式(数据层:芯片决定)

③ 通信协议(协议层:程序决定)

UART异步通信方式引脚连接方法:RXD:数据输入引脚。数据接受;TXD:数据发送引脚。数据发送。

TTL串口 & RS232 & RS485 & RS422

接口类型 逻辑1 典型值 逻辑0 典型值 说明 优缺点
TTL +15/3.3 0 一般MCU串口引脚都支持TTL
RS232 +15V -15V 3线全双工,点对点 接口电平高,传输速度相对较低,传输距离近
RS485 压差+(2~6)V 压差-(2~6)V 2线半双工,点对多,主从通信。使用压差传递信号。 传输速度高可达10M,抗干扰能力强,距离远。
RS422 相对比较少用

USB串口

USB串口

STM32 UART异步通信方式引脚:

串口号 RXD TXD
1 PA10(PB7) PA9(PB6)
2 PA3(PD6) PA2(PD5)
3 PB11(PC11/PD9) PB10(PC10/PD8)
4 PC11(PA1) PC10(PA0)
5 PD2 PC12
6 PC7(PG9) PC6(PG14)

STM32串口异步通信需要定义的参数:

① 起始位:1个逻辑0数据位开始

② 数据位(8位或者9位)

③ 奇偶校验位(第9位)

④ 停止位(1,1.5,2位)

⑤ 波特率设置

STM32串口异步通信需要定义的参数

UART串口波特率计算

STM32串口框图

STM32串口框图

小数波特率计算

USARTDIV

波特率寄存器

根据波特率和串口时钟频率,计算出USARTDIV的值。

DIV_Fraction=USART的小数部分 X16所得的整数

DIV_Mantissa=USART的整数部分

串口发送配置流程

HAL库中定义了串口寄存器描述

1
2
3
4
5
6
7
8
9
10
typedef struct
{
__IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */
__IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */
__IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
__IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */
__IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
__IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
__IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
} USART_TypeDef;

串口字节发送流程(寄存器版本):

①编程USARTx_CR1的M位来定义字长。

② 编程USARTx_CR2的STOP位来定义停止位位数。

③ 编程USARTx_BRR寄存器确定波特率。

④ 使能USARTx_CR1的UE位使能USARTx。

⑤ 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。具体请参考后面DMA实验。

⑥使能USARTx_CR1的TE位使能发送器。

⑦向发送数据寄存器TDR写入要发送的数据(对于M3,发送和接收共用DR寄存器)。

⑧向TRD寄存器写入最后一个数据后,等待状态寄存器USARTx_SR(ISR)的TC位置1,传输完成。

串口发送程序配置过程(HAL库) :

①初始化串口相关参数,使能串口:

1
HAL_UART_Init(UART_HandleTypeDef *huart);

UART_HandleTypeDef串口句柄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//串口句柄
typedef struct
{
USART_TypeDef *Instance; /*!< UART registers base address */

UART_InitTypeDef Init; /*!< UART communication parameters */

uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */

uint16_t TxXferSize; /*!< UART Tx Transfer size */

uint16_t TxXferCount; /*!< UART Tx Transfer Counter */

uint8_t *pRxBuffPtr; /*!< 指向接受数据的缓存地址Pointer to UART Rx transfer Buffer */

uint16_t RxXferSize; /*!< 接受的数据的数量UART Rx Transfer size */

uint16_t RxXferCount; /*!< 要接受的数据还剩多少UART Rx Transfer Counter */

DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */

DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */

HAL_LockTypeDef Lock; /*!< Locking object */

__IO HAL_UART_StateTypeDef State; /*!< UART communication state */

__IO uint32_t ErrorCode; /*!< UART Error code */

}UART_HandleTypeDef;

串口参数初始化结构体UART_InitTypeDef Init;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct
{
uint32_t BaudRate; /*波特率 */

uint32_t WordLength; /*字长 */

uint32_t StopBits; /*停止位 */

uint32_t Parity; /*奇偶校验位*/

uint32_t Mode; /*发送还是接受还是都开启 */

uint32_t HwFlowCtl; /*硬件流控制 */

uint32_t OverSampling; /*过采样*/
}UART_InitTypeDef;

②串口相关IO口配置,复用配置:在HAL_UART_MspInit中调用HAL_GPIO_Init函数。

③发送数据,并等待数据发送完成:HAL_UART_Transmit()函数;

1
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);//(句柄【加上地址符】,指针,数据的长度,超时时间)

有限性判断——可以查看函数提供的有限性判断确定要输入的值有哪些!

串口有效性判断

1
IS_UART_HWFLOW_INSTANCE(huart->Instance);
1
2
3
4
#define IS_UART_HWFLOW_INSTANCE(INSTANCE) (((INSTANCE) == USART1) || \
((INSTANCE) == USART2) || \
((INSTANCE) == USART3) || \
((INSTANCE) == USART6))

字长有效性判断

1
IS_UART_WORD_LENGTH(huart->Init.WordLength);
1
2
#define IS_UART_WORD_LENGTH(LENGTH) (((LENGTH) == UART_WORDLENGTH_8B) || \
((LENGTH) == UART_WORDLENGTH_9B))

串口发送过程函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "sys.h"
#include "delay.h"
#include "usart.h"

UART_HandleTypeDef usart1_handler;

void uart_init1()
{
usart1_handler.Instance=USART1;
usart1_handler.Init.BaudRate=115200;
usart1_handler.Init.WordLength=UART_WORDLENGTH_8B;
usart1_handler.Init.StopBits=UART_STOPBITS_1;
usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;
usart1_handler.Init.Mode=UART_MODE_TX_RX;
usart1_handler.Init.Parity=UART_PARITY_NONE;
HAL_UART_Init(&usart1_handler);
}
//初始化串口相关参数,使能串口:HAL_UART_INIT();

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Initure;

if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟

GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9

GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
//调用HAL_UART_INIT()函数时内部调用了HAL_UART_MspInit()函数
}
}
int main(void)
{
u8 buff[]="test";
HAL_Init();
Stm32_Clock_Init(360,25,2,8);
delay_init(180);

uart_init1();

while(1)
{
HAL_UART_Transmit(&usart1_handler,buff,sizeof(buff),1000);

delay_ms(300);
}
}

串口接收流程

串口接收流程(寄存器版本):

①编程USARTx_CR1的M位来定义字长。

② 编程USARTx_CR2的STOP位来定义停止位位数。

③ 编程USARTx_BRR寄存器确定波特率。

④ 使能USARTx_CR1的UE位使能USARTx。

⑤ 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。具体请参考后面DMA实验。

⑥使能USARTx_CR1的RE位为1使能接收器。

⑦如果要使能接收中断(接收到数据后产生中断),使能USARTx_CR1的RXNEIE位为1。

当串口接收到数据时:

① USARTx_SR(ISR)的RXNE位置1。表明移位寄存器内容已经传输到RDR(DR)寄存器。已经接收到数据并且等待读取。

②如果开启了接收数据中断(USARTx_CR1寄存器的RXNEIE位为1),则会产生中断。(程序上会执行中断服务函数)

③如果开启了其他中断(帧错误等),相应标志位会置1。

④读取USARTx_TDR(DR)寄存器的值,该操作会自动将RXNE位清零,等待下次接收后置位。

串口接收中断程序配置过程(HAL库):

①初始化串口相关参数,使能串口:HAL_UART_Init();//同发送

②串口相关IO口配置,复用配置:

在HAL_UART_MspInit中调用HAL_GPIO_Init函数。//同发送即配置GPIO口

③串口接收中断优先级配置和使能:

HAL_NVIC_EnableIRQ();

HAL_NVIC_SetPriority();

④使能串口接收中断:HAL_UART_Receive_IT();

1
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) //(句柄【加上地址符】,buffer,接受的数据量大小)

⑤编写中断服务函数:USARTx_IRQHandler

中断处理函数

1
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t tmp1 = 0, tmp2 = 0;

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE);
/* ------------------------------标志位------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_PEFLAG(huart);

huart->ErrorCode |= HAL_UART_ERROR_PE;
}

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART frame error interrupt occurred -------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_FEFLAG(huart);

huart->ErrorCode |= HAL_UART_ERROR_FE;
}

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART noise error interrupt occurred -------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_NEFLAG(huart);

huart->ErrorCode |= HAL_UART_ERROR_NE;
}

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
/* UART Over-Run interrupt occurred ----------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
__HAL_UART_CLEAR_OREFLAG(huart);

huart->ErrorCode |= HAL_UART_ERROR_ORE;
}

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
/* ---------接收到了数据,中断发生标志位------------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_Receive_IT(huart);
}

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE);
/* UART in mode Transmitter ------------------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_Transmit_IT(huart);
}

tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC);
tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC);
/* UART in mode Transmitter end --------------------------------------------*/
if((tmp1 != RESET) && (tmp2 != RESET))
{
UART_EndTransmit_IT(huart);
}

if(huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* Set the UART state ready to be able to start again the process */
huart->State = HAL_UART_STATE_READY;

HAL_UART_ErrorCallback(huart);
}
}

由中端服务函数调用中断处理函数判断不同的中断类型以调用不同处理函数——如下图

中断处理

串口接收中断流程

串口接收中断流程

调用回调函数,用户可以自己定义

1
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

串口接受过程函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "sys.h"
#include "delay.h"
#include "usart.h"

UART_HandleTypeDef usart1_handler; //串口句柄
u8 rdata[1]; //定义一个buffer

void uart_init1()
{
usart1_handler.Instance=USART1;
usart1_handler.Init.BaudRate=115200;
usart1_handler.Init.WordLength=UART_WORDLENGTH_8B;
usart1_handler.Init.StopBits=UART_STOPBITS_1;
usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;
usart1_handler.Init.Mode=UART_MODE_TX_RX;
usart1_handler.Init.Parity=UART_PARITY_NONE;
HAL_UART_Init(&usart1_handler);
}
//初始化串口相关参数,使能串口:HAL_UART_INIT();

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_Initure;

if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟

GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9

GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
/* ————————使能IO口————————————*/
HAL_NVIC_SetPriority(USART1_IRQn,3,3); //设置中断优先级,抢占优先级和响应优先级都为3
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能中断通道
}
}

//中断服务函数
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&usart1_handler);//中断处理函数
HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata));
}

//回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
u8 rec;
if(huart->Instance==USART1)
{
rec=*((huart->pRxBuffPtr)-1);
HAL_UART_Transmit(&usart1_handler,&rec,1,1000);
}
}


int main(void)
{
HAL_Init();
Stm32_Clock_Init(360,25,2,8);
delay_init(180);

uart_init1();
HAL_UART_Receive_IT(&usart1_handler,rdata,sizeof(rdata)); //使能接受中断
while(1)
{

}
}

中断服务函数和回调函数均为HAL库中定义的__weak弱函数,可由用户重新定义以实现其功能。

串口接受发送实验

接受的数据为ABCD……..(0x0D)(0x0A)格式,单片机才会接受,具体实现逻辑如下:

把每个接收到的数据保存在一个程序定义的Buffer数组中(数组长度为200),同时把接收到的数据个数保存在定义的变量中。程序通过对接收到的每个数据进行结束判断(接收到回车0x0d之后再接收到换行0x0a),程序接收结束之后,设置相应的标记位,标记结束。。。外部 循环通过判断标志位来判断程序结束,然后一次性通过串口1发送出来。发送完成之后,所有标志位和数据量都清零。

USART_RX_STA为一个16位的寄存器

USART_RX_STA
bit15 bit14 bit13~0
接收完成标志 接收到0X0D标志(置为1 接收到的有效数据个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)//如果是串口1
{
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d(bit14=1)
{
if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}

}
}
文章作者: jiangzuojiben
文章链接: http://jiangzuojiben.github.io/2019/11/10/UART%E2%80%94%E2%80%94%E4%B8%B2%E5%8F%A3%E8%AF%A6%E8%A7%A3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 jiangzuojiben