文章目录:
一、前言
约定:
- 本文所指串口通信,均为UART异步通信。
- 函数命名,不使用USART,统一使用UART,如UART1、UART2、UART3......。
为了使串口通信的代码编写更简单、移植更方便、适配更多的串口模块,
我们对各个串口的通信底层操作、收发工作,封装成了:bsp_UART.c 。
- 使用时,只需调用函数,不用理会底层代码设计,也无需CubeMX配置。
不用再管如何初始化,不用再管底层是什么机制,也不用管中断函数编写.....,只需调用函数。
每个串口的操作,6个函数,具体如下(以串口1为例):
void UART1_Init(uint32_t baudrate); // 初始化串口1; GPIO引脚PA9+PA10、中断优先级、通信参数:波特率可设、8位数据、无校验、1个停止位
void UART1_SendString(const char *string,...); // 发送字符串; 参数:字符串地址; 使用方法如同printf
void UART1_SendData(uint8_t *buffer, uint16_t num); // 发送指定数据; 参数:数据地址、字节数
uint16_t UART1_GetxNum(void); // 获取接收到的最新一帧字节数
uint8_t* UART1_GetRxData(void); // 获取接收到的数据(缓存的地址)
void UART1_ClearRx(void); // 清理接收到的数据(清理最后一帧字节数,因为它是判断接收的标志)
二、文件移植
1、打开示例文件夹,在随便一个示例的bsp文件夹中,把usart文件夹,复制到你的工程中。
2、添加bsp_UART.h文件路径; 新手操作如下图, 把h文件的文件夹路径添加进来.
3、添加 bsp_UART.c 文件到工程
操作如下图:
这里同时添加c和h文件,是为了能随时打开bsp_UART.h, 以方便查看有哪些函数可调用!
至此,你的工程,已经可以使用这个串口文件了。
三、初始化
使用这个已写好的文件,无需额外使用CubeMX进行配置,只管调用函数即可。
1、添加引用。
在需要使用串口通信的代码文件中,如,main.c,添加文件引用:
#include "bsp_UART.h"
2、初始化。
以使用UART1为例,调用函数:
UART1_Init (115200) ; // 参数:波特率
此函数内包括:引脚初始化、UART初始化、中断初始化。
不用管底层,bsp_UART.c 里面都已经封装、测试好了。
其它串口的初始化,方法相同。
串口1~6, 初始化时,将使用以下引脚:
串口端口 | 发送引脚 (TX) | 接收引脚 (RX) |
---|---|---|
UART1 | PA9 | PA10 |
UART2 | PA2 | PA3 |
UART3 | PB10 | PB11 |
UART4 | PC10 | PC11 |
UART5 | PC12 | PD2 |
UART6 | PC6 | PC7 |
四、发送
仅需2个函数,能发送所有类型数据, 如:float、int、数组、字符串、AT指令,等等。
发送机制:发送中断+环形队列。这些底层,不用管,只管调用已写好的函数。
void UART1_Init(uint32_t ulBaudrate); // 初始化串口1; GPIO引脚PA9+PA10、中断优先级、通信参数:波特率可设、8位数据、无校验、1个停止位
void UART1_SendString(const char *pcString, ...); // 发送字符串; 参数:字符串地址; 使用方法如同printf
① UART1_SendData (*data, num) ;
发送指定字节数的数据;参数:缓存地址、字节数
可用于发送数组、结构体、字符串等等。
② UART1_SendString (char *string,...) ;
发送字符串;不定长参数;使用如同printf
具体可以百度一下printf的格式化如何使用,这个函数,是一样的操作。
下面代码,展示这两个发送函数的各种操作 (以串口1为例,其它串口的操作,同理):
UART1_SendString("\r<<< USART1 接收到一帧数据 <<<\r"); // 发送字符串
UART1_SendString("字节数:%d 字节\r", rxNum); // 发送字符串,使用类似printf的格式化参数; 本行主要展示如何整合数值到字符串
UART1_SendString("数据 (ASCII): %s\r", (char *)rxData); // 以ASCII方式显示; 使用类似printf的格式化参数
UART1_SendString("数据(16进制): "); // 发送字符串
for (uint16_t i = 0; i < rxNum; i++) // 逐个字节输出
printf("0x%X ", rxData[i]); // 以16进制显示
UART1_SendData((uint8_t*)"\r\r", 3);
如你所见,使用bsp_UART.c里,printf也已处理好,直接可用,它已重定向至UART1。
五、接收
接收处理,共3个函数:
uint16_t UART1_GetRxNum(void); // 获取接收到的最新一帧字节数
uint8_t* UART1_GetRxdData(void); // 获取接收到的数据(缓存的地址)
void UART1_ClearRx(void); // 清理接收到的数据(清理最后一帧字节数,因为它是判断接收的标志)
接收机制上,使用:接收中断+空闲中断+双缓冲区。
这些底层工作,都已封装处理好,不用管,使用时只管调用函数。
① UART1_GetRxNum ( )
- 获取 最后一帧接收到的 字节数,返回值:字节数、数据类型 uint16_t
- 串口在调用 初始化函数 后,再调用这个函数判断返回值,即可知道接收状态:
- 当接收字节数 (返回值) ==0时:没有接收到数据;
- 当接收字节数 (返回值) > 0时:接收到新一帧数据 ;
② UART1_GetRxData ( )
- 获取 最后一帧接收到的 数据地址,返回值:数据类型 uint8_t* (注意:是指针);
- 如果你不会处理指针, 那,你可以把它当数组使用,用符号[ ]对数据进行访问。
- 指针是c语言最爽的操作,建议还是刨一刨指针:C语言 指针和数组
③ UART1_ClearRx ( ) ;
- 清0接收标志。其实就是清0 最后一帧的接收字节数。
- 每次接收到一帧数据,处理完了,就调用这函数,清0!
下面代码,示范如何接收、处理数据(以串口1为例,其它串口同理):
六、如何在CubeMX工程中使用
提供的bsp_UART.c和h文件,2024年7月后的版本,可同时适用于:标准库、HAL库的工程。
在资料文件夹的示例中,复制UART文件夹,粘贴至你的工程文件夹即可。
如果CubeMX生成的工程,还没有配置过串口,使用上面方法,移植文件、调用函数。
明确地:无需在CubeMX中做设置。
当然,使用CubeMX配置工程时,也可以使能相关串口,以方便标记哪些引脚被使用了。
重点注意两项:
① 使用上述表格的所用的串口引脚
② 在CubeMX或CubeIDE中只使能,不要打勾串口的中断、配置DMA,否则冲突。
当在CubeMX中使能串口后,就能使用HAL库的发送函数、接收函数:
HAL_UART_Transmit ( )
HAL_UART_Receive( )
只是,不建议使用了。因为bsp_USART.c的函数,使用上更方便。
还有一种特殊情况:
如果,某个串口,想使用CubeMX进行中断、DMA配置; 而某些串口,想使用文件中的函数:
可以的.
在bsp_UART.h文件中,修改下面的宏定义,给0值,即可。
宏定义 值解释:
0:自己另行配置、编写,例如使用CubeMX的中断、DMA配置等
1:使用文件的配置、函数