stm32HAL库 串口接收不定长数据(DMA传输)

相信大家很多初学者都会遇到串口接收不定长数据的情况。
对于初学者可能看着有点难理解,多看几遍就好,亲测能用。
话不多说上菜上菜!!!!

此代码是本人在具体工程应用,实测稳定。

有不足之处还请发现问题的小伙伴指出,大家共同进步。

此处省略stm32CubeIDE 配置过程*********************************
串口配置过程如下:
其他几个串口也是如下进行配置(正常使用的话只使能DMA接收即可)

波特率设置
波特率设置
NVIC中断配置
在这里插入图片描述
DMA配置
在这里插入图片描述

新建usr_uart_rec.h头文件,内容如下:

#ifndef USR_UART_REC_USR_UART_REC_H_
#define USR_UART_REC_USR_UART_REC_H_

#include "main.h"

#define MAX_REC_COUNT  36//可随根据实际使用情况进行调整

typedef struct
{
	uint8_t ReciveBuff[MAX_REC_COUNT];//接收缓冲区
	uint8_t uartRecCount;//接收数据的计数器
	uint8_t RecFlag:1;//接收标志位  主要用于数据处理
}DMAuartReciveBuff;


typedef struct
{
	DMAuartReciveBuff userUart1;
	DMAuartReciveBuff userUart2;
	DMAuartReciveBuff userUart3;
	DMAuartReciveBuff userUart4;
	DMAuartReciveBuff userUart5;
	DMAuartReciveBuff userUart6;
}UART_REC;


typedef struct
{
	uint8_t DMAbuff[MAX_REC_COUNT];
}uBbuffer;

typedef struct
{
	uBbuffer usr_uart1;
	uBbuffer usr_uart2;
	uBbuffer usr_uart3;
	uBbuffer usr_uart4;
	uBbuffer usr_uart5;
	uBbuffer usr_uart6;
}UART_MEMCPY_BUFF;//复制DMA的数据  用于解析

extern UART_MEMCPY_BUFF usr_dma_Data;
extern UART_REC userUart;

void USR_UART_Init();//串口初始化函数
void UART1_IRQ_DataGet();//在串口中断服务函数中添加此函数用于缓存数据
void UART2_IRQ_DataGet();
void UART3_IRQ_DataGet();
void UART4_IRQ_DataGet();
void UART5_IRQ_DataGet();
void UART6_IRQ_DataGet();

#endif /* USR_UART_REC_USR_UART_REC_H_ */

新建usr_uart_rec.c头文件,内容如下:


#include "main.h"
#include "usart.h"
#include "usr_uart_rec.h"
#include "string.h"



UART_REC userUart;
UART_MEMCPY_BUFF usr_dma_Data;


void USR_UART_Init()//串口初始化函数
{
	__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);//启动IDLE中断
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);//启动IDLE中断
	__HAL_UART_ENABLE_IT(&huart4,UART_IT_IDLE);//启动IDLE中断
	__HAL_UART_ENABLE_IT(&huart5,UART_IT_IDLE);//启动IDLE中断
	__HAL_UART_ENABLE_IT(&huart6,UART_IT_IDLE);//启动IDLE中断
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//启动IDLE中断

	userUart.userUart1.uartRecCount = 0;

//	  注意串口DMA 发送时候应该如下顺序初始化DMA 和串口
//	  MX_DMA_Init();
//	  MX_USART1_UART_Init();
//	  MX_UART4_Init();
//	  MX_UART5_Init();
//	  MX_USART2_UART_Init();
//	  MX_USART3_UART_Init();
//	  MX_USART6_UART_Init();

	HAL_UART_Receive_DMA(&huart1, (uint8_t *)&userUart.userUart1.ReciveBuff, sizeof(userUart.userUart1.ReciveBuff));
	HAL_UART_Receive_DMA(&huart2, (uint8_t *)&userUart.userUart2.ReciveBuff, sizeof(userUart.userUart2.ReciveBuff));
	HAL_UART_Receive_DMA(&huart3, (uint8_t *)&userUart.userUart3.ReciveBuff, sizeof(userUart.userUart3.ReciveBuff));
	HAL_UART_Receive_DMA(&huart4, (uint8_t *)&userUart.userUart4.ReciveBuff, sizeof(userUart.userUart4.ReciveBuff));
	HAL_UART_Receive_DMA(&huart5, (uint8_t *)&userUart.userUart5.ReciveBuff, sizeof(userUart.userUart5.ReciveBuff));
	HAL_UART_Receive_DMA(&huart6, (uint8_t *)&userUart.userUart6.ReciveBuff, sizeof(userUart.userUart6.ReciveBuff));
}



void UART1_IRQ_DataGet()//在串口1的中断服务函数中添加此函数用于缓存数据
{
	if(USART1 == huart1.Instance)
	{
		if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
		{
			HAL_UART_DMAStop(&huart1);
			userUart.userUart1.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//用于统计DMA接收到了多少数据
			userUart.userUart1.RecFlag = true;
//			printf("userUart.userUart1.uartRecCount is %d",userUart.userUart1.uartRecCount);
//			memcpy(&usr_dma_Data.usr_uart1.DMAbuff,&userUart.userUart1.ReciveBuff,userUart.userUart1.uartRecCount);
//			memset((uint8_t *)&userUart.userUart1.ReciveBuff,0,sizeof(userUart.userUart1.ReciveBuff));
//			userUart.userUart1.uartRecCount = 0;
			HAL_UART_Receive_DMA(&huart1, (uint8_t *)&userUart.userUart1.ReciveBuff, sizeof(userUart.userUart1.ReciveBuff));
			__HAL_UART_CLEAR_IDLEFLAG(&huart1);
		}
	}
}

void UART2_IRQ_DataGet()//在串口1的中断服务函数中添加此函数用于缓存数据
{
	if(USART2 == huart2.Instance)
	{
		if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE))
		{
			HAL_UART_DMAStop(&huart2);
			userUart.userUart2.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);//用于统计DMA接收到了多少数据
			userUart.userUart2.RecFlag = true;
//			printf("userUart.userUart2.uartRecCount is %d",userUart.userUart2.uartRecCount);
//			memcpy(&usr_dma_Data.usr_uart2.DMAbuff,&userUart.userUart2.ReciveBuff,userUart.userUart2.uartRecCount);
//			memset((uint8_t *)&userUart.userUart2.ReciveBuff,0,sizeof(userUart.userUart2.ReciveBuff));
//			userUart.userUart2.uartRecCount = 0;
			HAL_UART_Receive_DMA(&huart2, (uint8_t *)&userUart.userUart2.ReciveBuff, sizeof(userUart.userUart2.ReciveBuff));
			__HAL_UART_CLEAR_IDLEFLAG(&huart2);
		}
	}
}

void UART3_IRQ_DataGet()//在串口1的中断服务函数中添加此函数用于缓存数据
{
	if(USART3 == huart3.Instance)
	{
		if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE))
		{
			HAL_UART_DMAStop(&huart3);
			userUart.userUart3.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);//用于统计DMA接收到了多少数据
			userUart.userUart3.RecFlag = true;
//			printf("userUart.userUart3.uartRecCount is %d",userUart.userUart3.uartRecCount);
//			memcpy(&usr_dma_Data.usr_uart3.DMAbuff,&userUart.userUart3.ReciveBuff,userUart.userUart3.uartRecCount);
//			memset((uint8_t *)&userUart.userUart3.ReciveBuff,0,sizeof(userUart.userUart3.ReciveBuff));
//			userUart.userUart3.uartRecCount = 0;
			HAL_UART_Receive_DMA(&huart3, (uint8_t *)&userUart.userUart3.ReciveBuff, sizeof(userUart.userUart3.ReciveBuff));
			__HAL_UART_CLEAR_IDLEFLAG(&huart3);
		}
	}
}

void UART4_IRQ_DataGet()//在串口1的中断服务函数中添加此函数用于缓存数据
{
	if(UART4 == huart4.Instance)
	{
		if(__HAL_UART_GET_FLAG(&huart4,UART_FLAG_IDLE))
		{
			HAL_UART_DMAStop(&huart4);
			userUart.userUart4.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_uart4_rx);//用于统计DMA接收到了多少数据
			userUart.userUart4.RecFlag = true;
//			printf("userUart.userUart1.uartRecCount is %d",userUart.userUart1.uartRecCount);
//			memcpy(&usr_dma_Data.usr_uart4.DMAbuff,&userUart.userUart4.ReciveBuff,userUart.userUart4.uartRecCount);
//			memset((uint8_t *)&userUart.userUart4.ReciveBuff,0,sizeof(userUart.userUart4.ReciveBuff));
//			userUart.userUart4.uartRecCount = 0;
			HAL_UART_Receive_DMA(&huart4, (uint8_t *)&userUart.userUart4.ReciveBuff, sizeof(userUart.userUart4.ReciveBuff));
			__HAL_UART_CLEAR_IDLEFLAG(&huart4);

			//顺便处理收到的数据
		}
	}
}

void UART5_IRQ_DataGet()//在串口1的中断服务函数中添加此函数用于缓存数据
{
	if(UART5 == huart5.Instance)
	{
		if(__HAL_UART_GET_FLAG(&huart5,UART_FLAG_IDLE))
		{
			HAL_UART_DMAStop(&huart5);
			userUart.userUart5.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_uart5_rx);//用于统计DMA接收到了多少数据
			userUart.userUart5.RecFlag = true;
//			printf("userUart.userUart1.uartRecCount is %d",userUart.userUart1.uartRecCount);
//			memcpy(&usr_dma_Data.usr_uart5.DMAbuff,&userUart.userUart5.ReciveBuff,userUart.userUart5.uartRecCount);
//			memset((uint8_t *)&userUart.userUart5.ReciveBuff,0,sizeof(userUart.userUart5.ReciveBuff));
//			userUart.userUart5.uartRecCount = 0;
			HAL_UART_Receive_DMA(&huart5, (uint8_t *)&userUart.userUart5.ReciveBuff, sizeof(userUart.userUart5.ReciveBuff));
			__HAL_UART_CLEAR_IDLEFLAG(&huart5);
		}
	}
}




void UART6_IRQ_DataGet()//在串口1的中断服务函数中添加此函数用于缓存数据
{
	if(USART6 == huart6.Instance)
	{
		if(__HAL_UART_GET_FLAG(&huart6,UART_FLAG_IDLE))
		{
			HAL_UART_DMAStop(&huart6);
			userUart.userUart6.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_usart6_rx);//用于统计DMA接收到了多少数据
			userUart.userUart6.RecFlag = true;
//			printf("userUart.userUart1.uartRecCount is %d",userUart.userUart1.uartRecCount);
//			memcpy(&usr_dma_Data.usr_uart6.DMAbuff,&userUart.userUart6.ReciveBuff,userUart.userUart6.uartRecCount);
//			memset((uint8_t *)&userUart.userUart6.ReciveBuff,0,sizeof(userUart.userUart6.ReciveBuff));
//			userUart.userUart1.uartRecCount = 0;
			HAL_UART_Receive_DMA(&huart6, (uint8_t *)&userUart.userUart6.ReciveBuff, sizeof(userUart.userUart6.ReciveBuff));
			__HAL_UART_CLEAR_IDLEFLAG(&huart1);
		}
	}
}

注释掉的部分可用于调试,本人不建议在中断中处理大量数据,因为容易死机哦。

在使用串口DMA的时候需要注意以下问题:
在使用 stm32CubeIDE生成代码之后,按照官方的初始化流程有点点问题,会导致串口接收不到数据,所以要稍作修改,改变初始化流程:如下所示(最主要的是先初始化DMA 再初始化相关串口)这是一位博主针对DMA串口接收数据失败写的文章

  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_UART4_Init();
  MX_UART5_Init();
  MX_USART2_UART_Init();
  MX_USART3_UART_Init();
  MX_USART6_UART_Init();

最后在stm32f4xx_it.c 如下,将自定义的函数加到中断服务函数中即可 例如:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  UART1_IRQ_DataGet();
  /* USER CODE END USART1_IRQn 1 */
}

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
  UART2_IRQ_DataGet();
  /* USER CODE END USART2_IRQn 1 */
}

void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */

  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */
  UART3_IRQ_DataGet();
  /* USER CODE END USART3_IRQn 1 */
}

void UART4_IRQHandler(void)
{
  /* USER CODE BEGIN UART4_IRQn 0 */

  /* USER CODE END UART4_IRQn 0 */
  HAL_UART_IRQHandler(&huart4);
  /* USER CODE BEGIN UART4_IRQn 1 */
  UART4_IRQ_DataGet();
  /* USER CODE END UART4_IRQn 1 */
}

void UART5_IRQHandler(void)
{
  /* USER CODE BEGIN UART5_IRQn 0 */

  /* USER CODE END UART5_IRQn 0 */
  HAL_UART_IRQHandler(&huart5);
  /* USER CODE BEGIN UART5_IRQn 1 */
  UART5_IRQ_DataGet();
  /* USER CODE END UART5_IRQn 1 */
}

void USART6_IRQHandler(void)
{
  /* USER CODE BEGIN USART6_IRQn 0 */

  /* USER CODE END USART6_IRQn 0 */
  HAL_UART_IRQHandler(&huart6);
  /* USER CODE BEGIN USART6_IRQn 1 */
  UART6_IRQ_DataGet();  
  /* USER CODE END USART6_IRQn 1 */
}

需要注意的是 此代码在接收完成后会有一个软件标志位被置1,使用完数据之后需要用户手动清零。

userUart.userUart1.RecFlag = true;//串口1接收完成的中断标志

userUart.userUart1.uartRecCount = MAX_REC_COUNT - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//用于统计DMA接收到了多少数据

具体如何使用呢,举个例子(串口1):

uint8_t PowerRecBuff[23];

void PowerDataAnalyze()//解析电池的数据
{
	if(userUart.userUart1.RecFlag )
	{
		userUart.userUart1.RecFlag = false;//清除标志位
		memcpy((uint8_t *)&PowerRecBuff,(uint8_t *)&userUart.userUart1.ReciveBuff, userUart.userUart1.uartRecCount);
		RSOC = PowerRecBuff[23];
	/*
	使用者亦可在此处进行数据处理
	具体怎么处理不用我多说了吧---哈哈 加油。
	*/
		userUart.userUart1.uartRecCount = 0;
		memset((uint8_t *)&userUart.userUart1.ReciveBuff,0, sizeof(userUart.userUart1.ReciveBuff));
	}
}

自此完结。此代码亲测可用,对于初学者可能有点难理解,加油!共勉。

  • 10
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
STM32 HAL库中,可以使用DMA方式进行串口接收AD数据。可以使用函数HAL_UART_Receive_DMA来实现。该函数的参数包括UART句柄、接收数据缓冲区的指针和待接收数据的个数。通过调用该函数,可以在DMA方式下接收一定数量的数据。\[2\] 例如,可以使用以下代码来实现DMA方式下的串口接收AD数据: ```c uint8_t adData\[10\]; // 定义接收数据缓冲区 HAL_UART_Receive_DMA(&huart1, adData, 10); // 使用DMA方式接收10个字节的数据 ``` 在接收完成后,可以在DMA中断中调用串口接收中断回调函数HAL_UART_RxCpltCallback进行后续处理。在该回调函数中,可以对接收到的AD数据进行处理。\[2\] ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 在这里进行接收数据的处理 // adData数组中存放了接收到的AD数据 } ``` 需要注意的是,在使用DMA方式进行串口接收时,需要配置DMA通道和DMA传输方向。具体的配置可以参考STM32 HAL库的相关文档和例程。 #### 引用[.reference_title] - *1* *2* *3* [[016] [STM32] 串口HAL库轮询、中断、DMA方式传输数据](https://blog.csdn.net/kouxi1/article/details/123876915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值