上篇文章(串口通信简介-优快云博客)重点叙述了串口通信的通信协议,本文将重点叙述利用STM32F407单片机实现串口通信。(#注意:本文是基于HAL库的实现方式)
一、STM32F407串口简介
STM32F4 的串口分为两种:
①USART(即通用同步异步收发器);
②UART(即通用异步收发 器);
UART 是在 USART 基础上裁剪掉了同步通信功能,只剩下异步通信功能。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用串口通信基本都是异步通信。
STM32F4 有 4 个 USART 和 2 个 UART,其中 USART1 和 USART6 的时钟源来于 APB2 时钟,其最大频率为84MHz,其他4个串口的时钟源可以来于APB1时钟,其最大频率为42MHz。
二、USART结构图
① USART 信号引脚
TX:发送数据输出引脚;
RX:接收数据输入引脚;
SCLK:发送器时钟输出,适用于同步传输;
SW_RX:数据接收引脚,属于内部引脚,用于智能卡模式;
IrDA_RDI:IrDA 模式下的数据输入;
IrDA_TDO:IrDA 模式下的数据输出;
nRTS:发送请求,若是低电平,表示 USART 准备好接收数据;
nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送;
② 数据寄存器
USART_DR 包含了已发送或已接收的数据。由于它本身就是两个寄存器组成的,一个专门给发送用的(TDR),一个专门给接收用的(RDR),因此该寄存器具备读和写的功能。TDR 寄存器提供了内部总线和输出移位寄存器之间的并行接口。RDR 寄存器提供了内部总线和输入移位寄存器之间的并行接口。
当进行数据发送时,向 USART_DR 中写入的数据会自动存储在 TDR 内;当进行数据读取时,从 USART_DR 读数据会自动提取 RDR 的数据。
USART 数据寄存器(USART_DR)低 9 位数据有效,其他数据位保留。USART_DR 的第 9 位数据是否有效跟 USART_CR1 的 M 位设置有关,当 M 位为 0 时,表示 8 位数据字长;当 M 位 为 1 时,表示 9 位数据字长,一般使用 8 位数据字长。
当使能校验位(USART_CR1 中 PCE 位被置位)进行发送时,写到 MSB 的值(根据数据的长度不同,MSB 是第 7 位或者第 8 位)会被后来的校验位取代。
③ 控制器
USART 有专门控制发送的发送器,控制接收的接收器,还有唤醒单元、中断控制等等
④ 时钟与波特率
这部分的主要功能就是为 USART 提供时钟以及配置波特率。
波特率,即每秒钟传输的码元个数,在二进制系统中(串口的数据帧就是二进制的形式), 波特率与波特率的数值相等,所以我们今后在把串口波特率理解为每秒钟传输的二进制位数。
波特率通过以下公式得出:
“fck ”是串口的时钟,(USART2\3 和 UART4\5 的时钟源为 PCLK1,USART1\6 的时钟源为 PCLK2);
“USARTDIV”是一个无符号的定点数,存放在波特率寄存器(USART_BRR)的低 16 位,DIV_Mantissa[11:0]存放的是 USARTDIV 的整数部分,DIV_Fractionp[3:0]存放的是 USARTDIV 的小数部分;
例如:当串口 1 需要设置 115200 的波特率,且此时fck = 84MHz时,那么根据上式可得:
解得:USARTDIV ≈ 45.5729,
因此,USARTDIV 的整数部分为45,即0x2D,则DIV_Mantissa[11:0]=0x2D;小数部分为 0.5729,转换为十六进制,0.5729*16=9.1664,USARTDIV 允许有余数,对结果四舍五入取整,因此,DIV_Fractionp = 0x9。
三、USART 的配置步骤
1、把 USART_CR1 寄存器的 UE位 置位,以此激活 USART;
2、编程 USART_CR1 的 M 位,以此定义字长;
3、在 USART_CR2 中编程停止位的位数;
4、如果采用多缓冲器通信,配置 USART_CR3 中的 DMA 使能位(DMAT)。按多缓冲器通信中的描述配置 DMA 寄存器;
5、利用 USART_BRR 寄存器选择要求的波特率;
6、设置 USART_CR1 中的 TE 位,发送一个空闲帧作为第一次数据发送;
7、把要发送的数据写进 USART_DR 寄存器(此动作清除 TXE 位)。
#注意:在只有一个缓冲器的情况下,对每个待发送的数据重复步骤 7。
8、在 USART_DR 寄存器中写入最后一个数据字后,要等待 TC=1,它表示最后一个数据帧的传输结束。当需要关闭 USART 或需要进入停机模式之前,需要确认传输结束,避免破坏最后一次传输。
四、相关HAL库函数
1. HAL_UART_Init 函数
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
⚫ 函数描述
该函数用于初始化异步模式的收发器。
⚫ 函数形参
形参 1(UART_HandleTypeDef *huart)是结构体指针类型的串口句柄,其定义如下:
typedef struct
{
USART_TypeDef *Instance; /* UART 寄存器基地址 */
UART_InitTypeDef Init; /* UART 通信参数 */
UART_AdvFeatureInitTypeDef AdvancedInit; /* UART 高级功能配置结构体 */
uint8_t *pTxBuffPtr; /* 指向 UART 发送缓冲区 */
uint16_t TxXferSize; /* UART 发送数据的大小 */
__IO uint16_t TxXferCount; /* UART 发送数据的个数 */
uint8_t *pRxBuffPtr; /* 指向 UART 接收缓冲区 */
uint16_t RxXferSize; /* UART 接收数据大小 */ __IO
uint16_t RxXferCount; /* UART 接收数据的个数 */
uint16_t Mask; /* UART 数据接收寄存器掩码 */
DMA_HandleTypeDef *hdmatx; /* UART 发送参数设置(DMA) */
DMA_HandleTypeDef *hdmarx; /* UART 接收参数设置(DMA) */
HAL_LockTypeDef Lock; /* 锁定对象 */
_IO HAL_UART_StateTypeDef gState; /* UART 发送状态结构体 */
_IO HAL_UART_StateTypeDef RxState; /* UART 接收状态结构体 */
_IO uint32_t ErrorCode; /* UART 操作错误信息 */
}UART_HandleTypeDef;
1)Instance:指向 UART 寄存器基地址。实际上这个基地址 HAL 库已经定义好了,可以选择USART1~ USART3、USART6、UART4、UART5。
2)Init:UART 初始化结构体,用于配置通讯参数,如波特率、数据位数、停止位等等。将在下文叙述。
3)AdvancedInit:用于配置高级功能,如自动波特率,MSB 先行等。
4)pTxBuffPtr,TxXferSize,TxXferCount:分别是指向发送数据缓冲区的指针,发送数据的大 小,发送数据的个数。
5)pRxBuffPtr,RxXferSize,RxXferCount:分别是指向接收数据缓冲区的指针,接受数据的大 小,接收数据的个数。
6)Mask:UART 数据接收寄存器的掩码,用于存放数据的校验位。
7)hdmatx,hdmarx:配置串口发送接收数据的 DMA 具体参数。
8)Lock:对资源操作增加操作锁保护,可选 HAL_UNLOCKED 或者 HAL_LOCKED 两个参数。 如果 gState 的值等于 HAL_UART_STATE_RESET,则认为串口未被初始化,此时,分配锁资 源,并且调用 HAL_UART_MspInit 函数来对串口的 GPIO 和时钟进行初始化。
9)gState,RxState:分别是 UART 的发送状态、工作状态的结构体和 UART 接受状态的结构 体。HAL_UART_StateTypeDef 是一个枚举类型,列出串口在工作过程中的状态值,有些值只适用于 gState,如 HAL_UART_STATE_BUSY。
10)ErrorCode:串口错误操作信息。主要用于存放串口操作的错误信息。
UART_InitTypeDef 结构体类型,用于配置 UART 的各个 通信参数,包括波特率,停止位等,具体说明如下:
typedef struct
{
uint32_t BaudRate; /* 波特率 */
uint32_t WordLength; /* 字长 */
uint32_t StopBits; /* 停止位 */
uint32_t Parity; /* 校验位 */
uint32_t Mode; /* UART 模式 */
uint32_t HwFlowCtl; /* 硬件流设置 */
uint32_t OverSampling; /* 过采样设置 */
}UART_InitTypeDef
1)BaudRate:波特率设置。一般设置为 2400、9600、19200、115200。
2)WordLength:数据帧字长,可选 8 位或 9 位。这里我们设置为 8 位字长数据格式。
3)StopBits:停止位设置,可选 0.5 个、1 个、1.5 个和 2 个停止位,一般我们选择 1 个停止位。
4)Parity:奇偶校验控制选择,我们设定为无奇偶校验位。
5)Mode:UART 模式选择,可以设置为只收模式,只发模式,或者收发模式。
6)HwFlowCtl:硬件流控制选择,我们设置为无硬件流控制。
7)OverSampling:过采样选择,选择 8 倍过采样或者 16 倍过采样,一般选择 16 倍过采样。
⚫ 函数返回值
HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示成功,HAL_ERROR 表 示错误,HAL_BUSY 表示忙碌,HAL_TIMEOUT 超时。
2. HAL_UART_Receive_IT 函数
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart,
uint8_t *pData, uint16_t Size);
⚫ 函数描述
该函数用于开启以中断的方式接收指定字节。数据接收在中断处理函数里面实现。
⚫ 函数形参
形参 1(UART_HandleTypeDef *huart)是结构体指针类型的串口的句柄,其定义如上文所述。
形参 2(uint8_t *pData) 是要接收的数据地址。
形参 3(uint16_t Size) 是要接收的数据大小,以字节为单位。
⚫ 函数返回值
HAL_StatusTypeDef 枚举类型的值。
3. HAL_UART_IRQHandler 函数
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
⚫ 函数描述
该函数是 HAL 库中断处理公共函数,在串口中断服务函数中被调用,用于处理串口中断程序。
⚫ 函数形参
形参 1(UART_HandleTypeDef *huart)是结构体指针类型的串口的句柄,其定义如上文所述。
⚫ 函数返回值
无。
五、串口通信配置步骤
1)串口参数初始化(波特率、字长、奇偶校验等);
调用 HAL_UART_Init() 函数,进行串口参数初始化;
2)使能串口和GPIO时钟;
调用__HAL_RCC_USART1_CLK_ENABLE() 函数使能串口时钟;
调用__HAL_RCC_GPIOA_CLK_ENABLE() 函数使能GPIO时钟;
3)GPIO设置
调用 HAL_GPIO_Init() 函数,对GPIO模式设置;
4)开启串口相关中断,配置串口中断优先级
调用 HAL_UART_Receive_IT() 函数开启串口中断;
调用 HAL_NVIC_EnableIRQ() 函数使能串口中断;
调用 HAL_NVIC_SetPriority() 函数设置中断优先级;
5)编写中断服务函数
编写 HAL_UART_IRQHandler() 函数执行中断服务程序;
编写 HAL_UART_RxCpltCallback() 函数实现数据接收处理;
6)串口数据接收和发送
HAL_UART_Receive() 函数,用于接收数据;
HAL_UART_Transmit() 函数,用于发送数据;
六、串口通信程序流程图
main函数中的程序流程如下图所示,
usart.c文件中的程序流程图下图所示,