天天看點

STM32CubeMX:序列槽DMA

DMA:直接儲存區通路,DMA傳輸将資料從一個位址空間複制到另一個空間。DMA傳輸方式無需CPU直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢複現場過程,通過硬體為RAM何IO裝置開辟一條直接傳輸資料的通道,進而可以提高CPU的效率。一般用到DMA,主要作用為CPU減負。

我們用STM32CubeMX來配置一下序列槽的DMA實作收發。

選擇晶片:

STM32CubeMX:序列槽DMA

根據自己的電路設計情況配置時鐘,我用的外部晶振25M

STM32CubeMX:序列槽DMA

然後配置序列槽及DMA這裡我們配置兩個序列槽序列槽1和序列槽2,讓這兩個序列槽實作互通,兩個序列槽的配置一樣,這裡展示序列槽1的

STM32CubeMX:序列槽DMA
STM32CubeMX:序列槽DMA

其他配置和我們平時用序列槽配置一樣,就是在DMA這裡開啟收發就可以,點選Add添加,收發都添加進去,下面mode設定可以選擇Normal表單次傳輸,傳輸一次後終止傳輸,Circular表示循環傳輸,傳輸完成後又重新開始繼續傳輸,不斷循環永不停止。此處選擇單次傳輸。

Increment Address表示位址指針遞增。序列槽資料發送寄存器隻能存儲8bit,每次發送一個位元組,是以資料長度選擇Byte。

STM32CubeMX:序列槽DMA

最後這裡中斷要開啟,DMA的是預設開啟的,如果不開啟序列槽中斷,則程式隻能發送一次資料,程式不能判斷DMA傳輸是否完成,USART一直處于busy狀态。這樣我們就配置好了,序列槽2也如此配置,

STM32CubeMX:序列槽DMA

然後設定工程名稱及存放位址和使用的編譯器,點選生成。生成後點選打開工程,可以先編譯一下正常的是沒有錯誤的

發送比較簡單 用這個函數直接就可發送,

HAL_UART_Transmit_DMA();
	uint8_t TXbuff[] = "\r\n**** UART-Hyperterminal communication based on DMA ***\r\n ";
	HAL_UART_Transmit_DMA(&huart1, (uint8_t *)TXbuff, sizeof(TXbuff)-1);
           
STM32CubeMX:序列槽DMA

我們定義了一個數組,然後在while裡面循環每隔一秒發一下。

接收就比較麻煩,尤其是不定長的接受,這裡我們用到序列槽的空閑中斷,

首先我們要在庫函數添加一點代碼

/* UART in mode Idle -------------------------------------------------*/
if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
HAL_UART_IdleCpltCallback(huart);
return;
}
           

将上述代碼添加到stm32f4xx_hal_uart.c中,該檔案在工程的Drivers\STM32F4xx_HAL_Driver中添加位置:添加在

的前面,可ctrl+F彈出查找,輸入點選查找。

STM32CubeMX:序列槽DMA
__weak void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
}
           

将上述代碼添加到

__weak void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart)函數的前面。

STM32CubeMX:序列槽DMA

再接着

在stm32f4xx_hal_uart.h中添加

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);

STM32CubeMX:序列槽DMA

最後一步在在usart.c添加回調函數

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)

{

}

然後這裡我們在回調函數裡面直接做一個序列槽1和序列槽2的資料中轉

//定義一些變量和資料緩沖區
uint16_t RecCount1=0  ;      //接收資料個數
uint16_t RecCount2=0  ;      //接收資料個數

uint8_t UART_1_RxBuffer[1024];
uint8_t UART_2_RxBuffer[1024];

#define  LENGTH  1024     //接收緩存區大小,該值需要大于一幀資料的總字元數
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance == USART1)
	{

			HAL_UART_DMAStop(&huart1);

			//發生空閑中斷時,已接收資料個數等于資料總量減去DMA資料流中待接收的資料個數
			RecCount1 = LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

			HAL_UART_Transmit_DMA(&huart2,(uint8_t*)UART_1_RxBuffer,RecCount1);

			HAL_UART_Receive_DMA(&huart1,(uint8_t*)UART_1_RxBuffer,LENGTH);   //啟動DMA接收
		

		
	}
	if(huart->Instance == USART2)
	{
			HAL_UART_DMAStop(&huart2);

			//發生空閑中斷時,已接收資料個數等于資料總量減去DMA資料流中待接收的資料個數
			RecCount2 = LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);

			HAL_UART_Transmit_DMA(&huart1,(uint8_t*)UART_2_RxBuffer,RecCount2);

			HAL_UART_Receive_DMA(&huart2,(uint8_t*)UART_1_RxBuffer,LENGTH);   //啟動DMA接收



	}

}
           

然後在main函數的裡啟動DMA接收

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);    //使能IDLE中斷
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)UART_1_RxBuffer,LENGTH);    //啟動DMA接收
	__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);    //使能IDLE中斷
	HAL_UART_Receive_DMA(&huart2,(uint8_t*)UART_2_RxBuffer,LENGTH);    //啟動DMA接收
           
STM32CubeMX:序列槽DMA

編譯程式然後下載下傳通過序列槽發資料,我們會看到序列槽1發的資料序列槽2會收到,同時序列槽2發的資料序列槽1會收到。

STM32CubeMX:序列槽DMA

當然這個方法有個弊端,就是我們直接修改的是庫函數 ,那麼再次用STM32CubeMX生成代碼時stm32f4xx_hal_uart.h和stm32f4xx_hal_uart.c中修改的内容就沒有了,需要重新修改,其實庫函數的檔案在你每次改動STM32CubeMX後生成代碼時是不會變的,是以先把改好的這兩個檔案複制一下,然後在生成後直接粘貼過來替代就行。

繼續閱讀