stm32cubeMX 串口usart + DMA + FREERTOS配置 DMA接收任意长度数据

STM32cubeMX版本5.2.1

最近由于工作需要用到LWIP,看到网上有人说用cubeMX可以生成LWIP库,同时还能生成FREERTOS系统。就索性把整个工程的驱动库都换成了cubeMX生成的HAL库,在之前写工程基础上更改驱动配置,踩了一些坑,在这里把串口的配置及部分软件代码分享出来,希望能对大家有所帮助。

 

1、时钟配置

2、串口配置

 

3、FREERTOS配置

 

 

 

 

4、代码处理

①定义发送和接送的缓冲区

#define UART_BUFFSIZE    200   // 定义缓冲区的大小
typedef struct
{
	u16 Uart_SendLens;  //待发送数据长度
	u16 Uart_RecvLens;  //接收到的数据长度
	u16 RecvQue_Head;   //新接收数据环形队列头指针
	u16 RecvQue_Tail;   //新接收数据环形队列尾指针
	u8 Uart_SentBuff[UART_BUFFSIZE];
	u8 Uart_RecvBuff[UART_BUFFSIZE];
}UART_STR;

②串口初始化配置修改

UART_STR   Uart1_Str,Uart2_Str,Uart3_Str,Uart4_Str,Uart5_Str;  // 定义串口发送接收缓冲区
static void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  //以下为自己添加的中断配置
  /* USER CODE BEGIN USART1_Init 2 */
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 开启空闲中断
    // 接收DMA通道关联缓冲区
	HAL_UART_Receive_DMA(&huart1, Uart1_Str.Uart_RecvBuff, UART_BUFFSIZE); 
    // 以下这两个中断最好关掉,不然debug的时候会莫名其妙进中断,DMA发送不了
	__HAL_UART_DISABLE_IT(&huart1, UART_IT_ERR);
  __HAL_UART_DISABLE_IT(&huart1, UART_IT_PE);
  /* USER CODE END USART1_Init 2 */
}

③发送函数

/*
函数功能:串口1DMA数据发送
函数形参:Sendbuff :缓冲数据
          Bufflens :数据长度
函数返回值:数据长度
备注:无
*/
u16 Uart1_DMA_Sent(u8 * Sendbuff, u16 Bufflens)
{
	u16 l_val = Bufflens > UART_BUFFSIZE ? UART_BUFFSIZE : Bufflens;
	if(Bufflens <= 0)
	{
		return 0;
	}
	while(__HAL_DMA_GET_COUNTER(&hdma_usart1_tx));//检测DMA发送通道内还有没有数据
	if(Sendbuff)
	{
		memcpy(Uart1_Str.Uart_SentBuff, Sendbuff, l_val);
	}
	HAL_UART_Transmit_DMA(&huart1, Uart1_Str.Uart_SentBuff, l_val);
  return l_val;
}

④接收中断处理

/*
*函数功能:串口1接收中断函数
*/
void USART1_IRQHandler(void)
{
	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)
	{
		u16 l_val;
		l_val = UART_BUFFSIZE - DMA1_Channel5->CNDTR; // 通过DMA接收指针计算接收的字节数
		if(l_val > Uart1_Str.RecvQue_Tail)
		{
			Uart1_Str.Uart_RecvLens += l_val - Uart1_Str.RecvQue_Tail;
		}
		else
		{
			Uart1_Str.Uart_RecvLens += UART_BUFFSIZE - Uart1_Str.RecvQue_Tail + l_val;
		}
		Uart1_Str.RecvQue_Tail = l_val;
		Uart1_Str.Uart_RecvLens %= UART_BUFFSIZE;
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
	}
	HAL_UART_IRQHandler(&huart1); // 这一条保留就行了  
}

⑤接收函数

/*
函数功能:接收数据函数
函数形参:* Uart_Str : 串口数据缓冲结构地址
	    RcvBuff :接收数据缓冲区  
	    RevLen  :接收缓冲区长度
函数返回值:接收数据长度
备注:无
*/
static u16 Uart_Receive_Data(UART_STR * Uart_Str, u8 * RcvBuff, u16 RevLen)
{
	u16 l_val = 0;            // 局部变量   此次能拷贝的数据个数
	if(!RevLen || !Uart_Str->Uart_RecvLens)
	{
		return 0;
	}
	l_val = Uart_Str->Uart_RecvLens;
	
	if(l_val > RevLen)  // 缓冲区的数据数量比传进来的缓冲区容量大
	{
		l_val = RevLen;
	}
	if(Uart_Str->RecvQue_Tail > Uart_Str->RecvQue_Head)
	{
		memcpy(RcvBuff, Uart_Str->Uart_RecvBuff + Uart_Str->RecvQue_Head, l_val);
	}
	else
	{
		if((UART_BUFFSIZE - Uart_Str->RecvQue_Head) >= l_val)
		{
			memcpy(RcvBuff, Uart_Str->Uart_RecvBuff + Uart_Str->RecvQue_Head, l_val);
		}
		else
		{
			memcpy(RcvBuff, Uart_Str->Uart_RecvBuff + Uart_Str->RecvQue_Head, UART_BUFFSIZE - Uart_Str->RecvQue_Head);
			memcpy(RcvBuff + (UART_BUFFSIZE - Uart_Str->RecvQue_Head), Uart_Str->Uart_RecvBuff, l_val - (UART_BUFFSIZE - Uart_Str->RecvQue_Head));
		}
	}
	Uart_Str->Uart_RecvLens -= l_val;
	Uart_Str->RecvQue_Head += l_val;
	Uart_Str->RecvQue_Head %= UART_BUFFSIZE;
	return l_val;
}
/*
函数功能:从串口获取数据
函数形参:* Uartx :串口地址
         RcvBuff :接收缓冲指针
         RevLen  :接收缓冲区大小
函数返回值:接收数据长度
备注:无
*/
u16 Get_Uart_Data(USART_TypeDef* Uartx,u8 * RcvBuff, u16 RevLen)
{
	if(Uartx == USART1)
	{
		return(Uart_Receive_Data(&Uart1_Str, RcvBuff, RevLen));
	}
	else if(Uartx == USART2)
	{
		return(Uart_Receive_Data(&Uart2_Str, RcvBuff, RevLen));
	}
	else if(Uartx == USART3)
	{
		return(Uart_Receive_Data(&Uart3_Str, RcvBuff, RevLen));
	}
	else if(Uartx == UART4)
	{
		return(Uart_Receive_Data(&Uart4_Str, RcvBuff, RevLen));
	}
	else if(Uartx == UART5)
	{
		return(Uart_Receive_Data(&Uart5_Str, RcvBuff, RevLen));
	}
	return 0;
}

总结:发送采用单次的DMA发送,而接收则采用DMA循环接收,只要串口有收到数据DMA会自动帮你搬到内存中,地址会自动增加,当接收的数据个数到达设置的最大值时,DMA的接收指针又会重新指向接收buf的头指针。调用Get_Uart_Data()函数,会将最近接收的数据拷贝返回,拷贝的数据个数取决于接收的buf还有串口接收到的数据。这里只写出一个串口,除了串口5(没有DMA),其他的三个串口都可以参照上面写。

  • 10
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值