HAL庫學習筆記:五、使用DMA和串口空閒中斷實現不定長數據的接收

1. CubeMX配置

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

2. 程序配置

串口結構體

/* 串口數據類型 */
typedef struct 
{
	/* 數據接收標誌 */
	uint8_t usart_recv_flag;
	/* 數據解析長度 */
	uint16_t usart_recv_len;
	/* DMA 數據長度 */
	uint16_t usart_dma_recv_len;
	/* 數據解析緩衝區 */
	uint8_t usart_recv_buffer [USART_RECV_SIZE];
	/* DMA 數據緩衝區 */
	uint8_t usart_dma_recv_buffer [USART_DMA_RECV_SIZE];
}ts_usart_type;

串口變量定義

#define USART_DMA_RECV_SIZE     256
#define USART_RECV_SIZE  		1024
ts_usart_type usart_nb_type = {.usart_recv_flag = 0, .usart_recv_len = 0};

接收初始化

	/* 1. 打開串口空閒中斷 */
	__HAL_UART_ENABLE_IT(&huart1 ,UART_IT_IDLE );
	/* 2. 清除空閒中斷標誌 */
	__HAL_UART_CLEAR_IDLEFLAG(&huart1);
	/* 3. 打開 DMA 接收 */
	HAL_UART_Receive_DMA(&huart1 ,usart_nb_type.usart_dma_recv_buffer, USART_DMA_RECV_SIZE);

stm32f4xx_it.c

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	
	uint32_t temp;
	/* 1. 判斷空閒中斷 */
	if(__HAL_UART_GET_FLAG(&huart1 , UART_FLAG_IDLE) != RESET)
	{
		/* 2. 清除中斷 */
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
		/* 3. 停止 DMA 接收 */
		HAL_UART_DMAStop(&huart1);
		//清除 DMA 接收中斷,只需讀取一次狀態寄存器和數據寄存器,即可實現
		temp = huart1.Instance->SR;
		temp = huart1.Instance->DR;
		/* 4. 獲取 DMA 接收數據長度 : NDTR 爲遞減計數器,故 SIZE - NDTR*/
		temp = USART_DMA_RECV_SIZE - hdma_usart1_rx.Instance->NDTR;
		usart_nb_type.usart_dma_recv_len = temp;
		/* 5. 串口回調函數 */
		HAL_UART_RxCpltCallback(&huart1);
	}

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	
  /* 6. 開啓 DMA 接收 */
  HAL_UART_Receive_DMA(&huart1 ,usart_nb_type.usart_dma_recv_buffer ,USART_DMA_RECV_SIZE);
	
  /* USER CODE END USART1_IRQn 1 */
}

bsp_usart.c

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	/* 1. 判斷是否爲 NB 串口 */
	if(huart->Instance == huart1.Instance)
	{
		/* 2. 判斷是否有未處理的數據 */
		/* 有,從地址 0+old_len 開始獲取 DMA 接收區數據。 */
		if(usart_nb_type.usart_recv_len > 0)
		{
			rt_memcpy(&usart_nb_type.usart_recv_buffer[usart_nb_type.usart_recv_len] ,usart_nb_type.usart_dma_recv_buffer ,usart_nb_type.usart_dma_recv_len);
			usart_nb_type.usart_recv_len += usart_nb_type.usart_dma_recv_len;
		}
		/* 無,從地址 0 開始獲取 DMA 接收區數據。 */
		else
		{
			rt_memcpy(usart_nb_type.usart_recv_buffer ,usart_nb_type.usart_dma_recv_buffer ,usart_nb_type.usart_dma_recv_len);
			usart_nb_type.usart_recv_len = usart_nb_type.usart_dma_recv_len;
		}
		/* 3. 清空 DMA 接收區 */
		rt_memset(usart_nb_type.usart_dma_recv_buffer ,0 ,usart_nb_type.usart_dma_recv_len);
		/* 4. 清空 DMA 接收長度 */
		usart_nb_type.usart_dma_recv_len = 0;
		/* 5. 置位數據接收標誌 */
		usart_nb_type.usart_recv_flag = 1;
	}
}

main.c

		/* 1. 判斷是否有完整數據 */
		if(usart_nb_type.usart_recv_flag == 1)
		{
		/* 2. 打印數據到指定串口 */
			HAL_UART_Transmit(&huart2 ,usart_nb_type.usart_recv_buffer ,usart_nb_type.usart_recv_len ,100 );
		
		/* 3. 清空接收緩存區 */
			rt_memset(usart_nb_type.usart_recv_buffer ,0 ,usart_nb_type.usart_recv_len);
		
		/* 4. 清空數據接收標誌 */
			usart_nb_type.usart_recv_flag = 0;
		
		/* 5. 清空數據接收長度 */
			usart_nb_type.usart_recv_len = 0;			
		}

3. 備註

  • DMA 開啓後便始終處於接收數據狀態;
  • 觸發串口空閒中斷後,關閉 DMA 接收,並在回調函數中處理 DMA 數據;
  • 將處理好的數據轉移到數據緩存區,並設置標誌位,由主函數處理;
  • 緊接着開啓 DMA 接收,開始重複。

主函數檢測到標誌後,進行數據處理(此時如果在中斷函數中繼續接收數據,始終接收完整數據,應該沒有影響吧?),接着清空緩衝區,清除標誌位。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章