【嵌入式系統】DMA工作原理與常用函數解析
1、DMA基本原理
直接存儲器訪問通道(DMA, Direct Memory Access)用來提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。CPU只需初始化DMA,傳輸本身由DMA控制器來實現而無須CPU干預。DMA掛載在AHB上且數據傳輸前後不必保存上下文,因此數據可通過DMA高速移動。設置DMA的目的是:通過硬件爲存儲器和外設間開通若干個直接進行數據傳輸的通道,節約CPU資源。
2 DMA通道與配置
大容量STM32 MCU有兩個DMA控制器,共12個通道(DMA1有7個通道,DMA2有5個通道),通道的基本屬性如圖2所示
注
如圖4所示,DMA每個通道提前規定了特定外設和存儲器間的直接數據交換。例如,外設ADC1只能通過Access1與內存進行數據交換,在配置DMA源和目的基地址時要遵照圖4所示的預設規定。
由於DMA控制器一次只能開啓一個通道,因此若同一時間有多個來自不同通道的外設進行DMA請求,就需要通過通道優先級來使能高優先級通道(當優先級相同時,通道標號小的優先使能)。DMA控制器內部有一個仲裁器來協調各個DMA請求的優先權。
若配置內存外設數據單位相同,則從源地址處讀取一個單位數據包,往目的地址出寫一個相同寬度的數據包即可。若兩者單位不相同,就要參考“可編程的數據寬度”對照表進行數據傳輸操作。
3、DMA使用流程與相關函數
由於DMA通道需要配置的參數較多,因此使用結構體來簡化API輸入參數。因此這裏先根據需要配置結構體DMA_InitStructure,其成員變量如圖2所示均爲通道基本參數;再以DMA結構體指針作爲DMA_Init()的輸入參數,在其內部配置相應的寄存器。接下來對DMA_Init()函數作解析,其餘函數類似。
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
{
uint32_t tmpreg = 0;
/*--------------------------- DMAy Channelx CCR Configuration -----------------*/
/* Get the DMAy_Channelx CCR value */
tmpreg = DMAy_Channelx->CCR;
/* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */
tmpreg &= CCR_CLEAR_Mask;
/* Configure DMAy Channelx: data transfer, data size, priority level and mode */
/* Set DIR bit according to DMA_DIR value */
/* Set CIRC bit according to DMA_Mode value */
/* Set PINC bit according to DMA_PeripheralInc value */
/* Set MINC bit according to DMA_MemoryInc value */
/* Set PSIZE bits according to DMA_PeripheralDataSize value */
/* Set MSIZE bits according to DMA_MemoryDataSize value */
/* Set PL bits according to DMA_Priority value */
/* Set the MEM2MEM bit according to DMA_M2M value */
tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |
DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |
DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |
DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M;
/* Write to DMAy Channelx CCR */
DMAy_Channelx->CCR = tmpreg;
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
/* Write to DMAy Channelx CNDTR */
DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
/* Write to DMAy Channelx CPAR */
DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;
/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
/* Write to DMAy Channelx CMAR */
DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
}
首先配置DMA_CCR。由於CCR中包含了數據傳輸方向、數據單位等大量信息,爲便於修改和程序移植,函數定義了32bits的臨時寄存器tmpreg用於存儲這些信息。tmpreg首先捕獲當前DMA_CCR的位向量並使用掩碼CCR_CLEAR_Mask進行復位,防止置位時產生進位錯誤。事實上出於安全起見,置位操作前都應該掩去即將要設置的位,今後類似的做法不再贅述。將置位的tmpreg後傳給對應通道的CCR即可完成寄存器對應的配置。
接下來通過DMA結構體依次配置CNDTR(記錄傳輸數據大小)、CPAR(外設基地址)、CMAR(內存基地址)。