class="hljs-ln-code"> class="hljs-ln-line">HAL_UART_Transmit_IT (&huart1, uint8_t *pData, uint16_t Num); class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">HAL_UART_Transmit_DMA (&huart1, uint8_t *pData, uint16_t Num);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
先上板测试,后面再解释!
在/* USER CODE BEGIN 2 */ 与 /* USER CODE END 2 */ 之间,敲入以下发送代码:
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line"> static char strTem[100] = "Hello World!\r";
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit (&huart1, (uint8_t*)strTem, strlen(strTem), 0xFFFF);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit_IT (&huart1, (uint8_t*)strTem, strlen(strTem));
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line"> while((&huart1)->gState != HAL_UART_STATE_READY);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit_DMA (&huart1,(uint8_t*)strTem, strlen(strTem));
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line">
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
打开电脑的串口助手。
编译、烧录。串口助手马上有显示:
如果你那边,烧录后没有显示,要么是串口号错了,要么是没有打勾keil的自动复位。

下面,对3个函数的使用,逐一解释,不建议新手跳过,有避坑干货。
1、HAL_UART_Transmit (&huart1, uint8_t *pData, uint16_t Num, 超时值);
阻塞式发送。参数:串口,数据地址,发送的字节数,ms超时值
每发送一个字节,死等,好了继续发下一个,再死等,不断重复。
就是以前标准库种那最普通的死等法,只是它增加了一个超时值。
超时值:如果指定时间内没发送完毕,就直接返回,防止卡死。数据发送通信需时:
1秒 ÷ 波特率 × 字节数 × 10 × 1000ms。举例:115200波特率,100字节,大约用时 9ms。
新手如果不会计算,直接把超时值填大一点,如50ms。
2、HAL_UART_Transmit_IT (&huart1, uint8_t *pData, uint16_t Num);
利用中断发送。参数:串口,数据地址,发送的字节数
向寄存器填入一个字节,程序就继续干其它的事去,当一个字节发送完成后会产生发送中断,CubeMX生成的回调函数,自动填入下一个字节,不断重复,不用干预。
非阻塞式发送。能大大地减少程序运行时间的占用。
有一点要注意:当连续地调用本中断发送函数时,调用的间隔时间,小于通信所需的用时(按上),这时,后面那条函数调用,会直接返回,放弃发送。 因为函数内部,在发送前会判断串口的忙状态,如果在忙(还在发送上一包数据),就放弃本包数据,返回。
解决的方法,有两个:
① 最常用的,两行中断发送函数间,插入:HAL_Delay(10),原理参考上面的发送需时。
② 两行中断发送函数间,插入 while((&huart1)->gState != HAL_UART_STATE_READY); 和 HAL_Delay() 一样,都是死等,但能省了那么一点点运行时间。
3、HAL_UART_Transmit_DMA (&huart1, uint8_t *pData, uint16_t Num);
DMA发送。参数:串口,数据地址,发送的字节数。
上面的中断发送函数,100个字节,会产生100次中断。这个DMA发送函数,全程只产生一次中断。
调用后,函数给DMA数据地址,DMA就自动开始搬砖,它会把数据逐字节搬运到串口的DR寄存器上,等串口发送完这个字节了,再自动搬运下一个,过程完全不占用程序运行资源。搬完了,就产生一个中断,给程序打个招呼。通常,我们程序上,把这个“招呼”也省略了,不用理会它。
3个发送函数中,推荐使用这个DMA发送函数,发送的最优解。
同样的,两行DMA发送函数间,注意发送间隔,否则放弃发送直接返回。处理方法同上。
四、printf 重定向到USART1
约定俗成地,常使用printf函数,输出一些调试信息。它能很灵活地控制输出字符串的格式。
约定俗成地,printf 常通过 USART1 输出数据到串口助手,而非USART2、3...。
为什么要使用printf,而不直接使用上面那几个发送函数?
因为printf能控制格式!如,在字符串中插入变量值,而上面的发送函数就没它方便了;
另外 ,printf通常用于调试阶段。当调试完成了,我们无除一行行地查找和取消printf的输出,只要注释了重定向函数中的发送函数行,即可取消整个工程中的printf调试信息输出。
要使用 printf ,需要做两个事:
① 在文件头,插入: #include "stdio.h" ;
② 重定向 printf, 使它能通过 USART1 输出。
把下面代码,复制到 main.c的 BEGIN 4 与 END 4 注释行之间,即可使用。
无需打勾“Use MicroLIB"。
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">#include
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">#pragma import(__use_no_semihosting)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">struct __FILE
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">{
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line"> int handle;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line">};
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line">FILE __stdout;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line">void _sys_exit(int x)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line">{
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="12"> class="hljs-ln-code"> class="hljs-ln-line"> x = x;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="13"> class="hljs-ln-code"> class="hljs-ln-line">}
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="14"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="15"> class="hljs-ln-code"> class="hljs-ln-line">int fputc(int ch, FILE *f)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="16"> class="hljs-ln-code"> class="hljs-ln-line">{
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="17"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="18"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x02);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="19"> class="hljs-ln-code"> class="hljs-ln-line"> return ch;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="20"> class="hljs-ln-code"> class="hljs-ln-line">}
class="hide-preCode-box">
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
现在,试试我们的 printf 输出效果:
在刚才敲入代码的位置,在3个发送函数之前,添加一行printf, 尝试输出系统运行时钟的值。
整体如下:
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line"> printf("\r系统运行时钟:%d MHz\r", SystemCoreClock/1000000);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line"> static char strTem[100] = "Hello World!\r";
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit (&huart1, (uint8_t*)strTem, strlen(strTem), 0xFFFF);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit_IT (&huart1, (uint8_t*)strTem, strlen(strTem));
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line"> while((&huart1)->gState != HAL_UART_STATE_READY);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UART_Transmit_DMA (&huart1,(uint8_t*)strTem, strlen(strTem));
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line">
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
串口助手的输出效果,如下图,能正常输出了!

然后,我们来试个错,把printf这行,剪切到3个发送函数之下。再编译,烧录运行。
怎样,没有输出了吧?!
原因、解决的方法,如上面发送函数那段所述,不再重述。 这里提出,只是为了大家有更好的机制理解。
五、接收代码的编写
发送数据可以调用现成的函数,而接收数据,现成函数不太好用。
接收也有3个函数,和发送的3个函数相对应:
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">HAL_UART_Receive (&huart1, uint8_t *pData, uint16_t Num, 超时值);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">HAL_UART_Receive_IT (&huart1, uint8_t *pData, uint16_t Num);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">HAL_UART_Receive_DMA (&huart1, uint8_t *pData, uint16_t Num);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
一般,大家都不使用这三个函数,太TM的难用了,有兴趣的可csdn搜它们的使用优劣分析。
我们利用HAL库现成的资源,另敲十来行代码,令串口的接收机制:更实用、更灵活。
完成后,整个接收过程,将全程自动接收,在外部判断是否收到新数据即可。
操作上共分4小项,下面将有详细操作图解:
① 定义一个结构体变量:存放接收的字节数、数据数组。
② 开启DMA:让硬件自动接收数据放到缓存
③ 重写回调函数:当一帧数据接收好了,把缓存的数据,转存到全局结构体变量里,备用。
④ 在需要使用串口接收的地方,如在while中,判断接收字节数>0, 即为接收到新一帧数据了。
1、定义一个结构体变量:存放接收的字节数、数据
① 首先,在main.h文件,新建一个结构体类型
如何打开main.h?
方法1:在main.c中,右击空白位置,弹出菜单中选择:Toggle Header/Code File
方法2:在文件树中,点击main.c左边的+号,即可看到关联的文件,双击其中的main.h。

在 /* USER CODE BEGIN ET */ 与 /* USER CODE END ET */ 之间,新建一个结构体类型。

新声明的结构体类型,它有3个成员:
uint16_t RxNum; // 接收字节数,只要字节数>0,即为接收到新一帧数据
uint8_t RxData[512]; // 接收到的数据(对外使用)
uint8_t RxTemp[512]; // 临时缓存(对内作接收),在DMA空闲中断中将把其内容复制到RxData[ ]
有些网上的教程,还会有一个Flag成员变量,用作标记是否接收到数据。 无需Flag变量,直接判断 RxNum > 0 即可,更直接、清晰。
为什么要用两个缓存?双缓存结构,能增加数据处理上的时间空间,以避免单缓存在未及时处理数据时又被新的接收过程所覆盖。
另外 ,在定义结构体类型的下面一行代码中,用extern声明了一个结构体变量,它将在main.c中定义。
技巧:
- 当希望声明的结构体类型能被全局可调用,就在h文件中定义,其它文件引用这个h文件。
- 当希望定义的变量能被全局可调用,可在c文件中定义,然后在h中用extern声明,其它文件引用这个h文件。
- 上述声明结构体时用x作名称开头,这不是语法的规定,只是个人命名习惯,表示这个变量是一个结构体。
② 回到main.c,定义结构体变量
在 /* USER CODE BEGIN 0 */ 与 /* END 0 */ 之间,用刚才声明的结构体类型,定义我们的结构体变量。

(这里同时定义了1~6的结构体变量,只是为了方便编写示例。)
现在,我们拥有一个了全局变量:xUART1。
以后的其它文件,如蓝牙模块驱动、串口屏驱动,只要在其文件中引用:main.h,就能随时通过这个结构体变量,使用串口1接收的数据了。
上述结构体的声明、定义,还有全局变量的使用方法,新手注意理解,做项目时相当好使。
2、开启DMA,让硬件自动接收数据
在main()函数的初始化部分,增加一行代码,使DMA接收开始工作:
HAL_UARTEx_ReceiveToIdle_DMA (串口、缓存、字节数) ;
参数:串口、接收缓存区、最大接收字节数
作用:使能DMA、使能串口的空闲中断,正式进入接收状态。
操作:在 main.c的 /* USER CODE BEGIN 2 */ 与 /* END 2 */ 之间,插入函数:
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUART1.BuffTemp, sizeof(xUART1.RxTemp));
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
插入后的位置,如下图:

调用函数后,硬件就会立刻进入自动接收状态:从RX引脚接收到的数据,会逐个字节顺序存放到指定缓存中,这里我们指定的缓存是:xUART1.RxTemp。
因为函数内部,开启了DMA中断、空闲中断,所以达成下列两个条件之一,就会触发中断:
① DMA接收的字节数,达到了参数中的最大值
② 串口发生空闲中断,即RX引脚,超过1字节的时间,没有新信号。
当上述中断产生时,硬件自动调用其相关的中断服务函数,再继而调用回调函数。
CubeMX生成的代码,已编写好上述两个中断服务函数,还定义了一个它俩最终调用的回调函数。注意,这个回调函数是一个弱函数。
因此,我们不用管中断服务函数,只需重写这个回调函数,就能实现对接收数据的处理。
3、重写DMA空闲中断回调函数
DMA完成中断、空闲中断,所调用的回调函数:
HAL_UARTEx_RxEventCallback(串口,接收到的字节数);
这个弱函数,定义在stm32xxxx_hal_uart.c文件的底部。
不用管它!
在工程中其它位置编写一个相同名称的函数后,这个弱函数就无效了,编译时会忽略它。
现在,我们对它进行重写,以实现对接收数据的处理。
在main.c的底部,/* USER CODE BEGIN 4 */ 与 /* END 4 */ 之间,编写此函数,并编写其代码。(下面函数,可以直接复制粘贴到工程中。)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="12"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="13"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="14"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="15"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="16"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="17"> class="hljs-ln-code"> class="hljs-ln-line">void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="18"> class="hljs-ln-code"> class="hljs-ln-line">{
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="19"> class="hljs-ln-code"> class="hljs-ln-line"> if (huart == &huart1)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="20"> class="hljs-ln-code"> class="hljs-ln-line"> {
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="21"> class="hljs-ln-code"> class="hljs-ln-line"> __HAL_UNLOCK(huart);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="22"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="23"> class="hljs-ln-code"> class="hljs-ln-line"> xUART1.RxNum = Size;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="24"> class="hljs-ln-code"> class="hljs-ln-line"> memset(xUART1.RxData, 0, sizeof(xUART1.RxData));
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="25"> class="hljs-ln-code"> class="hljs-ln-line"> memcpy(xUART1.RxData, xUART1.RxTemp, Size);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="26"> class="hljs-ln-code"> class="hljs-ln-line"> HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUART1.RxTemp, sizeof(xUART1.RxTemp));
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="27"> class="hljs-ln-code"> class="hljs-ln-line"> }
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="28"> class="hljs-ln-code"> class="hljs-ln-line">}
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="29"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="30"> class="hljs-ln-code"> class="hljs-ln-line">
class="hide-preCode-box">
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
重点解释上面代码的后四行:
① xUART1.RxNum = Size;
把接收的字节数,存入结构体 xUART1.RxNum,以备使用 。
在程序的其它地方,判断 RxNum > 0, 就能知道是否收到新一帧数据了。
② memset(xUART1.RxData, 0, sizeof(xUART1.RxData));
清0数组中的数据,准备存入新数据
③ memcpy(xUART1.RxData, xUART1.RxTemp, Size);
把新数据,从临时缓存中,复制到xUART1.RxData[], 以备使用
从结构体和这段回调函数中,可以发现,这是一个双缓存的操作思路。
.RxData:用于存放接收后完整的一帧数据,对外使用 。
.RxTemp:用于DMA接收过程,是一个中间缓存。
④ HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUART1.RxTemp, sizeof(xUART1.RxTemp));
再次开启DMA空闲中断,进入接收状态。
我们在main()函数的初始化部分,已调用过这个函数了,为什么要在回调函数中再次调用?
因为在DMA的中断服务函数里,会关闭DMA,即只接收一次。所以,在接收完一帧后,再次调用函数,就能让DMA开始工作接收下一帧。在这个位置调用 ,能让DMA不断地循环工作。
其实,在CubeMX配置中,DMA有一个选项 :Mode的circular, 可以让DMA进行连续地的工作,接收完成后,无需在回调函数里再次开启DMA 。但是,目前的CubeMX版本(V6.10),这个参数的选择,会使我们上面的DMA接收与发送,相冲突。那我们二选一好了,自行手工调用。
注意一点:本篇的处理,是保存最后一帧数据。当有新一帧数据来了,会自动盖掉旧帧数据。
至此,接收工作已准备妥当。程序运行后,硬件、程序会自动开始接收工作,并把接收的帧数据,存放到结构体中: 最新一帧的字节数保存在xUART1.RxNum, 最新一帧的数据保存在xUART1.RxData。
4、接收的使用示范
我们来试试使用的效果吧!
① 在main.c的while函数里,判断xUART1.RxNum, 即可知道是否收到新数据。
具体操作方法如下:
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="1"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="2"> class="hljs-ln-code"> class="hljs-ln-line"> while (1)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="3"> class="hljs-ln-code"> class="hljs-ln-line"> {
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="4"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="5"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="6"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="7"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="8"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="9"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="10"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="11"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="12"> class="hljs-ln-code"> class="hljs-ln-line"> if (xUART1.RxNum)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="13"> class="hljs-ln-code"> class="hljs-ln-line"> {
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="14"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="15"> class="hljs-ln-code"> class="hljs-ln-line"> uint16_t rxNum = xUART1.RxNum;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="16"> class="hljs-ln-code"> class="hljs-ln-line"> uint8_t *rxData = xUART1.RxData;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="17"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="18"> class="hljs-ln-code"> class="hljs-ln-line"> printf("\r<<<<< UART1 接收到一帧数据 \r");
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="19"> class="hljs-ln-code"> class="hljs-ln-line"> printf("字节数:%d \r", rxNum);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="20"> class="hljs-ln-code"> class="hljs-ln-line"> printf("ASCII : %s\r", (char *)rxData);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="21"> class="hljs-ln-code"> class="hljs-ln-line"> printf("16进制: ");
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="22"> class="hljs-ln-code"> class="hljs-ln-line"> for (uint16_t i = 0; i < rxNum; i++)
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="23"> class="hljs-ln-code"> class="hljs-ln-line"> printf("0x%X ", rxData[i]);
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="24"> class="hljs-ln-code"> class="hljs-ln-line"> printf("\r\r");
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="25"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="26"> class="hljs-ln-code"> class="hljs-ln-line"> xUART1.RxNum = 0;
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="27"> class="hljs-ln-code"> class="hljs-ln-line"> }
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="28"> class="hljs-ln-code"> class="hljs-ln-line">
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="29"> class="hljs-ln-code"> class="hljs-ln-line"> }
- class="hljs-ln-numbers"> class="hljs-ln-line hljs-ln-n" data-line-number="30"> class="hljs-ln-code"> class="hljs-ln-line">
class="hide-preCode-box">
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}" onclick="hljs.signin(event)">
② 工程,编译,烧录!
③ 打开串口助手,参数设置 115200-None-8-1, 打开对应的串口端口。
按一下板子右下角的复位键,串口输出,如下图:

③ 在串口的发送区,输入字符串 "天气不错喔~~",或者其它数据。
点击发送:串口助手将通过PA10,发送到开发板。在程序的while函数中,那段代码判断接收到数据后,为了方便观察,将通过USART1的PA9发出数据,串口助手接收后,显示如下:

④ 试试16进制数据的发送。
发送区:打勾16进制发送,输入随意16进制值,不用加0x,用空格作间隔。
注意,16进制的值,不一定是ASCII码表的显示范围值,所以在ASCII显示中,会出现乱码,正常现象。

至此,USART1的收发,已完整地展示完毕。
如有错漏,欢迎留言指正修改~~
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/qq_49053936/article/details/135517022","extend1":"pc","ab":"new"}">>
评论记录:
回复评论: