前言:
前面的两篇文章都说关于发送的,HAL库发送数据可以调用现成的函数,而接收数据,现成函数不太好用。这里为了记录了一下自己参考了网上几个大佬的代码,整理了一下USART1 DMA方式接受数据的代码,这里亲测了一下,传输比较稳定,也没有出现发送数据过快导致串口反应不过来的情况。
正文开始:
Cubemx配置
这里跟上一篇博客一样,我就不再赘述了
额外注意的一点是记得勾选上Use MicroLIB
代码编写:
这里我是学习了大佬的博客,传送门
我稍微做了一下改进,这里还是沿用上一篇文章创建的两个文件 USART_DMA.c和USART.h
按照大佬博客里教的。
在USART_DMA.h中,我们声明一下我们的结构体
typedef struct //声明一个结构体,方便管理变量
{
uint16_t ReceiveNum; //接受字节数;在中断回调函数中被自动赋值;只要字节数>0,即为接受到新一帧
uint8_t ReceiveData[512]; //接受到的数据
uint8_t BuffTemp[512]; //接受缓存;注意,这个数组只是一个缓存
//临时缓存,在DMA空闲中断中将把一帧数据复制到ReceivedData[]
}myUATR_TypeDef;
然后在USART_DMA.c中定义一个自己的变量,如果要在其他.c文件引用的,extern一下就行
myUATR_TypeDef myUSART1 = {0}; //用来定义自己的变量
我采用的方法是,直接将extern myUATR_TypeDef myUSART1;放在USART_DMA.h里面。
开启DMA,让硬件自动接收数据,.
我们整个接收过程,仅使用到1个HAL库函数。只需在main()函数的初始化部分,调用HAL库函数:HAL_UARTEx_ReceiveToIdle_DMA (串口、缓存、字节数) ;
参数:串口、接收缓存区、最大接收字节数
作用:使能DMA、使能串口的空闲中断,正式进入接收状态。
我们需要在main.c中添加代码
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, myUSART1.BuffTemp, sizeof(myUSART1.BuffTemp)); // 开启DMA空闲中断
重写DMA空闲中断回调函数,(DMA完成中断、空闲中断,所调用的回调函数): HAL_UARTEx_RxEventCallback(串口,接收到的字节数);
弱函数定义在stm32xx_hal_gpio.c文件的底部。
/******************************************************************************
* 函 数: HAL_UARTEx_RxEventCallback
* 功 能: DMA+空闲中断回调函数
* 参 数: UART_HandleTypeDef *huart // 触发的串口
* uint16_t Size // 接收字节
* 返回值: 无
* 备 注: 1:这个是回调函数,不是中断服务函数。技巧:使用CubeMX生成的工程中,中断服务函数已被CubeMX安排妥当,我们只管重写回调函数
* 2:触发条件:当DMA接收到指定字节数时,或产生空闲中断时,硬件就会自动调用本回调函数,无需进行人工调用;
* 2:必须使用这个函数名称,因为它在CubeMX生成时,已被写好了各种函数调用、函数弱定义(在stm32xx_hal_uart.c的底部); 不要在原弱定义中增添代码,而是重写本函数
* 3:无需进行中断标志的清理,它在被调用前,已有清中断的操作;
* 4:生成的所有DMA+空闲中断服务函数,都会统一调用这个函数,以引脚编号作参数
* 5:判断参数传进来的引脚编号,即可知道是哪个串口接收收了多少字节
******************************************************************************/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart == &huart1) // 判断串口
{
__HAL_UNLOCK(huart); // 解锁串口状态
myUSART1.ReceiveNum = Size; // 把接收字节数,存入结构体xUSART1.ReceiveNum,以备使用
memset(myUSART1.ReceiveData, 0, sizeof(myUSART1.ReceiveData)); // 清0前一帧的接收数据
memcpy(myUSART1.ReceiveData, myUSART1.BuffTemp, Size); // 把新数据,从临时缓存中,复制到xUSART1.ReceiveData[], 以备使用
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, myUSART1.BuffTemp, sizeof(myUSART1.BuffTemp)); // 再次开启DMA空闲中断; 每当接收完指定长度,或者产生空闲中断时,就会来到这个
}
}
本篇的处理,是保存最后一帧数据。当有新一帧数据来了,会自动盖掉旧帧数据。
接下来,为了验证,到底这个项目的程序到底好事使不好使,我在这里写了一个测试函数,其中的myprintf函数是用了我上一篇文章的写法
void USART_test()
{
if(myUSART1.ReceiveNum) //一旦接受到数据
{
myUSART1.ReceiveNum = 0; //将数据清零
if(strcmp((char *)myUSART1.ReceiveData,"hello\r\n") == 0)//相等,返回 0;
{
num++;
myprintf("接受次数%d\r\n",num);
}
}
}
将其放在while循环中
最终效果演示
在串口助手这里,简单设置一下 ,一定要注意的是,如果点了发送新行之后,一定不要再额外在第一个箭头的后面加回车了,否则是接受不到的。
接受的速度也很不错,没有出现过卡死的状况,100ms回应一次。