本系列主要讲解STM32CubeHAL的使用,详细的安装部署教程请见【STM32】STM32 CubeMx使用教程一–安装教程-CSDN博客
串口相关原理请移步标准库笔记
UART
工程创建
1设置RCC
- 设置高速外部时钟HSE 选择外部时钟源

2设置串口

- 1点击USATR1
- 2设置MODE为异步通信(Asynchronous)
- 3基础参数:波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1 接收和发送都使能
- 4GPIO引脚设置 USART1_RX/USART_TX
- 5 NVIC Settings 一栏使能接收中断

3设置时钟

我的是 外部晶振为8MHz
- 1选择外部时钟HSE 8MHz
- 2PLL锁相环倍频72倍
- 3系统时钟来源选择为PLL
- 4设置APB1分频器为 /2
代码
HAL库UART函数库介绍
UART_HandleTypeDef结构体
1 | |
- Instance指针: 用于指向用户使用的串口寄存器基地址;
- Init串口初始化结构体: 用于配置串口的通讯参数,如波特率、串口数据位数、停止位等等。详细参数说明,请看下面初始化结构体的分析;
- AdvancedInit串口高级功能配置结构: 用于配置串口的高级功能,如自动波特率,MSB先行等等功能。本章节暂时用不到,所以不详细进行讲解;
- pTxBuffPtr, TxXferSize,TxXferCount:分别是需要发送数据的地址指针,发送数据的大小以及需要发送的数据个数;
- pRxBuffPtr, RxXferSize,RxXferCount:分别是指向存放数据的地址指针,接受数据的大小,接受数据的个数;
- Mask: 串口接受寄存器的掩码,用于存放数据的校验位,与初始化结构体中的Parity参数有关;
- hdmatx, hdmarx结构体:配置串口发送接受数据的DMA具体参数;
- Lock:串口对象资源锁,该结构体主要负责分配锁资源,可选择HAL_UNLOCKED或者是HAL_LOCKED两个参数。如果gState的值等于HAL_UART_STATE_RESET, 则认为串口未被初始化,此时,分配锁资源,并且调用HAL_UART_MspInit函数来对串口的GPIO和时钟进行初始化,代码见“stm32h7xx_hal_uart.c文件”。 这部分的代码需要用户自己编写,用于实现串口底层配置的功能。在HAL库中,函数调用了一个UNUSED函数,该函数其实是宏定义,主要是为了防止编译提示警告。
1 | |
- gState,RxState:分别是串口的发送状态、工作状态的结构体和串口接受状态的结构体。HAL_UART_StateTypeDef是一个枚举类型, 列出串口在工作过程中的状态值,有些值只适用于gState,如HAL_UART_STATE_BUSY;
- ErrorCode:串口错误操作信息。主要用于存放串口操作的错误信息。
接下来,我们看一下UART_InitTypeDef这个结构体类型,该结构体用于配置串口的通讯方式,内嵌于UART_HandleTypeDef结构体中,具体说明如下:
UART_InitTypeDef初始化结构体(stm32h7xx_hal_uart.h文件)
1 | |
- BaudRate:波特率设置。 一般设置为2400、9600、19200、115200。标准库函数会根据设定值计算得到USARTDIV值,见公式 20‑1,并设置USART_BRR寄存器值。
- WordLength:数据帧字长, 可选8位或9位。它设定USART_CR1寄存器的M位的值。如果没有使能奇偶校验控制,一般使用8数据位;如果使能了奇偶校验则一般设置为9数据位。
- StopBits:停止位设置, 可选0.5个、1个、1.5个和2个停止位,它设定USART_CR2寄存器的STOP[1:0]位的值,一般我们选择1个停止位。
- Parity:奇偶校验控制选择, 可选USART_Parity_No(无校验)、USART_Parity_Even(偶校验)以及USART_Parity_Odd(奇校验),它设定USART_CR1寄存器的PCE位和PS位的值。
- Mode:USART模式选择, 有USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定USART_CR1寄存器的RE位和TE位。
- HwFlowCtl: 硬件流控制选择,只有在硬件流控制模式下才有效,可选有:使能RTS、使能CTS、同时使能RTS和CTS、不使能硬件流。
- OverSampling : 过采样选择,选择8倍过采样或者16过采样。
- OneBitSampling: 一个采样位方法使能。可选择三个采样位方法或者一个采样位方法。
- Prescaler: 串口时钟分频因子。默认选择不分频。
- FIFOMode:FIFO模式。是否使用FIFO模式。
- TXFIFOThreshold:发送FIFO的阈值。当达到设定的阈值时,将数据发送给TX移位寄存器。阈值的值可以为容量1/8,1/4,1/2,3/4,7/8,满。
- RXFIFOThreshold:接受FIFO的阈值。当达到设定的阈值时,将数据给接受寄存器。阈值的值可以为容量1/8,1/4,1/2,3/4,7/8,满。
串口发送/接收函数
- HAL_UART_Transmit();串口发送数据,使用超时管理机制
- HAL_UART_Receive();串口接收数据,使用超时管理机制
- HAL_UART_Transmit_IT();串口中断模式发送
- HAL_UART_Receive_IT();串口中断模式接收
- HAL_UART_Transmit_DMA();串口DMA模式发送
- HAL_UART_Transmit_DMA();串口DMA模式接收
这几个函数的参数基本都是一样的,我们挑两个讲解一下
串口发送数据:
1 | |
功能:串口****发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
- *pData 需要发送的数据
- Size 发送的字节数
- Timeout 最大发送时间,发送数据超过该时间退出发送
中断接收数据:
1 | |
功能:串口中断接收,以中断方式接收指定长度数据。
大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
- *pData 接收到的数据存放地址
- Size 接收的字节数
串口中断函数
- HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
- HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
- HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
- HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
- HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
- HAL_UART_ErrorCallback();串口接收错误函数
*串口接收中断回调函数:*
1 | |
功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码,
串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改,
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
串口中断处理函数
1 | |
功能:对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用
如果接收数据,则会进行接收中断处理函数
1 | |
如果发送数据,则会进行发送中断处理函数
1 | |
串口查询函数
HAL_UART_GetState(); 判断UART的接收是否结束,或者发送数据是否忙碌
举例:
1 | |
重新定义printf函数
- 在 stm32f4xx_hal.c中包含#include <stdio.h>
1 | |
在 stm32f4xx_hal.c 中重写fget和fput函数
/** * 函数功能: 重定向c库函数printf到DEBUG_USARTx * 输入参数: 无 * 返 回 值: 无 * 说 明:无 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch; } /** * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx * 输入参数: 无 * 返 回 值: 无 * 说 明:无 */ int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, &ch, 1, 0xffff); return ch; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
在main.c中添加
```cpp
#define RXBUFFERSIZE 256
char RxBuffer[RXBUFFERSIZE];
while (1)
{
/* USER CODE END WHILE */
printf("测试\n");
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
之后便可以使用Printf函数和Scanf,getchar函数
UART接收中断
因为中断接收函数只能触发一次接收中断,所以我们需要在中断回调函数中再调用一次中断接收函数
具体流程:
1、初始化串口
2、在main中第一次调用接收中断函数
3、进入接收中断,接收完数据 进入中断回调函数
4、修改HAL_UART_RxCpltCallback中断回调函数,处理接收的数据,
5 回调函数中要调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断
函数流程图:
HAL_UART_Receive_IT(中断接收函数) -> USART2_IRQHandler(void)(中断服务函数) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) -> HAL_UART_RxCpltCallback(huart);(中断回调函数)
HAL_UART_RxCpltCallback函数就是用户要重写在main.c里的回调函数。
代码实现:
并在main.c中添加下列定义:
1 | |
在main()主函数中,调用一次接收中断函数
1 | |
在main.c下方添加中断回调函数
1 | |
字符发送
USB转串口硬件设计3 字符发送函数
1 | |
Usart_SendString函数用来发送一个字符串,它实际是调用HAL_UART_Transmit函数(这是一个阻塞的发送函数,无需重复判断串口是否发送完成)发送每个字符, 直到遇到空字符才停止发送。最后使用循环检测发送完成的事件标志来实现保证数据发送完成后才退出函数。