參考文章
https://blog.csdn.net/as480133937/article/details/104927922
DMA簡介
DMA,全稱Direct Memory Access,即直接存儲器訪問。DMAC 即 DMA 控制器,提供了一種硬件的數據傳輸方式,無需 CPU 的介入,可以處理外設和存儲器之間或者存儲器和存儲器之間的傳輸數據。因無 CPU 介入,從而使 CPU 可以專注在處理其他系統功能上。由此可以減輕CPU的負擔,使得更多的CPU資源分配到其他的任務處理上面去。
主要配置參數
了解DMA是什么,以及為什么要使用DMA后,我們之后就是要知道如何去配置DMA,以及需要配置哪些參數。下面是一些主要需要配置的參數。
DMA的傳輸通道
不同單片機有不同的通道可供使用,結合實際資源參數進行選擇。
目標外設和源外設號(有些可能是直接配置地址)
DMA的傳輸方式
- 外設到內存
- 內存到外設
- 內存到內存
- 外設到外設
數據傳輸寬度
這個可以根據實際需求配置成一個字節、半個字、全字這三種方式
- DMA_SRC_WIDTH_BYTE????
- DMA_SRC_WIDTH_HALF_WORD
- DMA_SRC_WIDTH_WORD???
模式設置
- 模式可以設置為正常模式(開啟之后僅只傳輸一次)
- 循環模式(會一直循環傳輸數據)
地址生成方式
所謂地址生成方式,就是看你使不使用,地址遞增或者固定地址。也就是
固定模式和遞增模式。
- 在固定模式中,地址一直固定為初始化的基地址(DMACCxSrcAddr、 DMACCxDestAddr)。
- 在遞增模式中,下一次傳輸數據的地址是當前地址加 1(或者 2, 4),這個值取決于數據
- 傳輸寬度。
具體配置代碼
void DMA_SPITransmit_Init(void)
{DMA_SPIT_Handle.Instance = DMA_Channel1;DMA_SPIT_Handle.Init.Data_Flow = DMA_DATA_FLOW_M2P;DMA_SPIT_Handle.Init.Request_ID = REQ3_SPI2_SEND;DMA_SPIT_Handle.Init.Mode = DMA_NORMAL;DMA_SPIT_Handle.Init.Source_Inc = DMA_SOURCE_ADDR_INCREASE_ENABLE;DMA_SPIT_Handle.Init.Desination_Inc = DMA_DST_ADDR_INCREASE_DISABLE;DMA_SPIT_Handle.Init.Source_Width = DMA_SRC_WIDTH_BYTE;DMA_SPIT_Handle.Init.Desination_Width = DMA_DST_WIDTH_BYTE;/*-----------------------------------------------------------------------------------*//* Note:If user dons not apply interrupt, Set DMA_ITC_Callback?¢DMA_IE_Callback NULL *//*-----------------------------------------------------------------------------------*/DMA_SPIT_Handle.DMA_ITC_Callback = NULL;DMA_SPIT_Handle.DMA_IE_Callback = NULL;HAL_DMA_Init(&DMA_SPIT_Handle);__HAL_LINK_DMA(SPI_Handle, HDMA_Tx, DMA_SPIT_Handle);
}
我這里使用的是SPI+DMA配置方式,單片機還是航芯的單片機,選擇了寄存器地址(目標地址)不變,源地址(數據地址)遞增,這樣配置滿足我們需求。然后傳輸完成回調函數和傳輸出錯回調函數暫時沒有配置。
DMA中斷
每個 DMA 通道都有一個專用的中斷。中斷事件有兩種類型:傳輸完成和傳輸錯誤。 每一個中斷事件在狀態寄存器和清除寄存器中有專用的標志位。通過檢測兩種中斷標志位可以獲取我們DMA的傳輸情況。
__weak void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{uint32_t lu32_Channel_Index;/* Get DMA Channel number */lu32_Channel_Index = ((uint32_t)(hdma->Instance) - (uint32_t)(DMA_Channel0)) / 0x20;/* Channel has been interrupted */if (DMA->INT_STATUS & (1 << lu32_Channel_Index)){/* Transfer complete interrupt */if (DMA->INT_TC_STATUS & (1 << lu32_Channel_Index)){DMA->INT_TC_CLR |= (1 << lu32_Channel_Index);if (NULL != hdma->DMA_ITC_Callback){hdma->DMA_ITC_Callback();}}/* Transfer error interrupt */if (DMA->INT_ERR_STATUS & (1 << lu32_Channel_Index)){DMA->INT_ERR_CLR |= (1 << lu32_Channel_Index);if (NULL != hdma->DMA_IE_Callback){hdma->DMA_IE_Callback();}}}}
上面就是在觸發中斷后在中斷函數中處理函數的中斷程序,主要邏輯就是先獲取哪個中斷觸發,然后判斷觸發源(傳輸完成或者傳輸錯誤)。
總結
DMA配置使用起來參數不多也比較簡單,但是要真正使用并理解還是需要時間去消化。像之前不使用DMA不知道有啥作用,只模糊地知道可以幫CPU減負,導致很多情況下都亂用DMA。