STM32 串口DMA收发不定长数据(cubeMx)

1、配置SYS、RCC以及时钟树

STM32 串口DMA收发不定长数据(cubeMx)_第1张图片

2、配置串口

串口选择异步通信,波特率115200,数据位8,停止位1,无校验;添加TX、RX DMA,DMA配置为Normal模式,最后使能串口全局中断入口函数。

STM32 串口DMA收发不定长数据(cubeMx)_第2张图片

STM32 串口DMA收发不定长数据(cubeMx)_第3张图片

STM32 串口DMA收发不定长数据(cubeMx)_第4张图片

3、生成工程

保存选择.c与.h分开,然后确定工程名称、保存路径及工程类型,此处为MDK5的工程。

STM32 串口DMA收发不定长数据(cubeMx)_第5张图片

STM32 串口DMA收发不定长数据(cubeMx)_第6张图片

4、源码添加

a.printf支持

#define USART1_Printf
int fputc(int ch,FILE *f)
{
	#if defined USART1_Printf
		HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
	#elif defined USART3_Printf
		HAL_UART_Transmit(&huart3,(uint8_t *)&ch,1,0xFFFF);
	#else
		return ch;
	#endif
	
	return ch;
}

b.串口 DMA收发缓存区配置

#define UART_DMA_BUFF_LEN_MAX	255

uint8_t Uart1RxBuff[UART_DMA_BUFF_LEN_MAX];
uint8_t Uart1TxBuff[UART_DMA_BUFF_LEN_MAX];
uint16_t Uart1RxBuffLen = 0;

uint8_t Uart3RxBuff[UART_DMA_BUFF_LEN_MAX];
uint8_t Uart3TxBuff[UART_DMA_BUFF_LEN_MAX];
uint16_t Uart3RxBuffLen = 0;

void USRT_DMA_IDLE_Start()
{
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);        //使能IDLE中断
	HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);    //开启DMA接收
	
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart3,Uart3RxBuff,UART_DMA_BUFF_LEN_MAX);
}

c.串口接收回调

在stm32f1xx_it.c文件中,于串口中断函数调用串口回调函数,该函数自己实现。

//引用外部串口回调函数
extern void UART_Rx_Callback(UART_HandleTypeDef *huart);


/**
  * @brief This function handles USART1 global interrupt.
  */
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 */
  UART_Rx_Callback(&huart1);         //调用串口接收回调
  /* USER CODE END USART1_IRQn 1 */
}

/**
  * @brief This function handles USART3 global interrupt.
  */
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 */
  UART_Rx_Callback(&huart3);        //调用串口接收回调
  /* USER CODE END USART3_IRQn 1 */
}

d.回调函数IDLE中断

IDLE中断用于帧判断。

void UART_Rx_Callback(UART_HandleTypeDef *huart)
{
	//判断IDLE中断
	if(!__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE))
	{
		return;
	}
	//清除IDLE中断--直接读取SR、DR寄存器即可清除IDLE中断
	__HAL_UART_CLEAR_IDLEFLAG(huart);
//	temp = huart->Instance->SR;
//	temp = huart->Instance->DR;
	
	//判断中断源
	if(huart == &huart1)
	{
		UART1_Rx_IDLE_Callback();
	}
	else if(huart == &huart3)
	{
		UART3_Rx_IDLE_Callback();
	}
}

e.处理串口数据

串口接收完数据通过DMA回发。

void UART1_Rx_IDLE_Callback()
{
	//暂停DMA
	HAL_UART_DMAStop(&huart1);
	
	//获取DMA缓存区剩余长度
//	Uart3BuffLen_DMA = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
	
	//实际长度 = 总长度-剩余长度
	Uart1RxBuffLen = UART_DMA_BUFF_LEN_MAX-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
	
	//回发测试
	UART_Transmit_DMA(&huart1,Uart1RxBuff,Uart1RxBuffLen);
	
	//开始DMA接收
	HAL_UART_Receive_DMA(&huart1,Uart1RxBuff,UART_DMA_BUFF_LEN_MAX);
}
void UART3_Rx_IDLE_Callback()
{
	//暂停DMA
	HAL_UART_DMAStop(&huart3);
	
	//获取DMA缓存区剩余长度
//	Uart3BuffLen_DMA = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
	
	//实际长度 = 总长度-剩余长度
	Uart3RxBuffLen = UART_DMA_BUFF_LEN_MAX-__HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
	
	UART_Transmit_DMA(&huart3,Uart3RxBuff,Uart3RxBuffLen);
	//开始DMA接收
	HAL_UART_Receive_DMA(&huart3,Uart3RxBuff,UART_DMA_BUFF_LEN_MAX);
}


HAL_StatusTypeDef UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
	uint8_t *TxBuff = NULL;
	
	if(huart == &huart1)
	{
		TxBuff = Uart1TxBuff;;
	}
	else if(huart == &huart3)
	{
		TxBuff = Uart3TxBuff;
	}
	
	memmove(TxBuff,pData,Size);

        //开始发送
	return HAL_UART_Transmit_DMA(huart,TxBuff,Size);
}

f.注意事项

发送数据长度低于接收缓存区长度;

接收数据时,若数据超出缓存区长度则产生数据丢失、错误等现象;本次DMA配置为Normal,超出缓存区长度的数据直接丢失不接收数据长度为缓存区长度;若配置为Circlur模式,则超出部分数据会继续接收,覆盖缓存区旧数据,数据长度为实际长度对缓存区长度求余;若缓存区满,则调用回调函数“HAL_UART_RxCpltCallback”;

发送数据时若上一次发送缓存区未发送完成,则新数据会覆盖旧数据导致数据发送失败、错误;数据发送完成,则调用回调函数“HAL_UART_TxCpltCallback”;

g.解决方法

接收时,增大接收缓存区,或者分包发送,保证自定义串口协议中数据包最大长度低于缓存区长度;

或者DMA接收设为Circlur模式,合理利用以下函数,及时将缓存区数据读出,避免数据被覆盖;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);        //缓存区满时调用
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);    //缓存区存储一半时调用 

 

发送时,注意发送完成回调函数:“HAL_UART_TxCpltCallback”,完成一次发送之后再通过标志位、信号量等通知下一次数据发送。

 

你可能感兴趣的:(STM32 串口DMA收发不定长数据(cubeMx))