DMA,全稱為:Direct Memory Access,即直接存儲器訪問。DMA傳輸方式無需CPU 直接控制傳輸,也沒有中斷處理方式那樣保留現場和恢復現場的過程,通過硬件為RAM 與I/O設備開辟一條直接傳送數據的通路,能使CPU 的效率大為提高。
一、DMA請求映像
STM32F10x有兩個DMA控制器,使用DMA控制器可使數據從存儲器到存儲器、存儲器到外設、外設到存儲器。每個控制器有若干通道,參考《STM32參考手冊》,各通道請求一覽如下圖:
二、DMA初始化
1、使能DMA時鍾
__HAL_RCC_DMA1_CLK_ENABLE(); //DMA1時鍾使能
2、關聯DMA與UART1
DMA_HandleTypeDef UART1TxDMA_Handler; //DMA句柄 __HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler); //將DMA與USART1聯系起來(發送DMA)
3、配置DMA句柄
//Tx DMA配置 UART1TxDMA_Handler.Instance=chx; //通道選擇 通道4指的是UART1Tx UART1TxDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH; //存儲器到外設 /*由於是從存儲器讀數據給外設,所以存儲器設置為增量模式,這樣的話,它地址可以自動增加;而外設因為是固定的地址,所以設為非增量模式。*/ UART1TxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外設非增量模式 UART1TxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //存儲器增量模式 /*外設數據長度和存儲器數據長度要設置一樣的位數,其可以定義一次傳輸數據量的大小*/ UART1TxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外設數據長度:8位 UART1TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存儲器數據長度:8位 UART1TxDMA_Handler.Init.Mode=DMA_NORMAL; //外設普通模式 UART1TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等優先級 HAL_DMA_DeInit(&UART1TxDMA_Handler); HAL_DMA_Init(&UART1TxDMA_Handler);
其中 Instance 參數根據請求映像表需要用到哪個設備就選擇相應通道; Init.Direction 可選擇“存儲器到外設”、“外設到存儲器”、“存儲器到存儲器”,決定數據傳輸方向; Init.PeriphInc 參數選擇外設是否增量模式,本例中使用外設串口1的發送端,所以地址是固定的,要使用非增量模式;Init.MemInc 參數選擇存儲器是否為增量模式,由於存儲器地址是連續的,本例中要讀取連續的地址區域,所以要使用增量模式;Init.PeriphDataAlignment和Init.MemDataAlignment 分別表示外設和存儲器長度,兩個長度要相同,否則可能讀取不完全;其余參數lue。
4、開啟DMA傳輸
//開啟一次DMA傳輸 //huart:串口句柄 //pData:傳輸的數據指針 //Size:傳輸的數據量 void MYDMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size);//開啟DMA傳輸 huart->Instance->CR3 |= USART_CR3_DMAT;//使能串口DMA發送 }
我中使用到了寄存器編程使能DMA發送。開啟DMA傳輸時直接調用該函數即可。
5、其他API
__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TC4) //傳輸完成返回1,否則返回0
__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TC4);//清除DMA1通道4傳輸完成標志
HAL_UART_DMAStop(&UART1_Handler); //傳輸完成以后關閉串口DMA
__HAL_DMA_GET_COUNTER(&UART1TxDMA_Handler);//得到當前還剩余多少個數據
6、(補充)DMA的接收
在用cubemx配置DMA的時候,要注意配置一下RX的mode,配置為Circular,意思是DMA接收處於循環接受狀態,否則DMA只能接收一次。
在使用DMA接收的時候,使用函數
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
注意:如果在cubemx設置為循環接收模式,該函數可以不放在while循環里;如果沒有設置為循環接收模式(即設置為normal模式),需要放在while里循環是能DMA接收中斷。
DMA接收中斷使用回調函數
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
實測使用回掉函數void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma)的話不知道為什么進入不了中斷(即進入不了這個回掉函數),不知道為什么,希望有大神來解答一下,謝謝。
三、總結
使用DMA傳輸可以使大量的數據傳輸交給DMA控制器執行,CPU空閑出來做其他的事,提高了運行效率。