STM32的DMA(Direct Memory Access,直接存儲器存取)是一種在單片機中用于高效實現數據傳輸的技術。它允許外設設備直接訪問RAM,不需要CPU的干預,從而釋放CPU資源,提高CPU工作效率,本文基于STM32F429、Hal庫詳細分析DMA的特性與使用。
1.DMA的特性
(1)STM32F429擁有兩個DMA控制器(DMA1和DMA2),DMA1 控制器 AHB 外設端口不連接到總線矩陣,因此,只有?DMA2 數據流能夠執行存儲器到存儲器的傳輸,外設包括片內外設和片外外設。
片內外設:例如GPIO(通用輸入輸出端口)、USART(通用同步/異步收發器)、I2C(Inter-Integrated Circuit,一種串行通信總線)、SPI(Serial Peripheral Interface,串行外設接口)等。這些外設通過DMA控制器,可以直接與內存(如SRAM、Flash等)進行數據傳輸。
片外外設:這是指通過外部接口連接到STM32芯片的設備,例如DHT11溫度傳感器、MQ2煙霧傳感器等。這些設備通過相應的接口(如ADC、SPI等)與STM32連接,然后通過DMA控制器進行數據傳輸,要確保片外外設支持DMA功能才可以用DMA傳輸數據。
存儲器到存儲器的傳輸意味著DMA控制器可以直接從一個內存區域(如RAM的一個數組或緩沖區)讀取數據,并將這些數據寫入到另一個內存區域(同樣是RAM的另一個數組或緩沖區)。這種傳輸模式在多種應用中都非常有用,比如:
-
數據移動:如果你需要將一段數據從一個位置移動到另一個位置,使用DMA可以節省CPU時間,因為CPU不需要參與數據的傳輸過程。
-
數據處理:在圖像處理、音頻處理或任何需要處理大量數據的應用中,你可能需要將數據從一個緩沖區讀取出來,進行一些處理(如濾波、FFT等),然后將結果寫回另一個緩沖區。使用DMA可以并行地進行數據的讀取和寫入,從而加速整個處理過程。
-
數據橋接:在某些應用中,你可能需要從一個硬件接口讀取數據,然后將其寫入到另一個硬件接口。使用DMA可以確保這兩個接口之間的數據傳輸是高效的,不會受到CPU中斷或其他任務的影響。
-
循環緩沖區:在處理實時數據流時,循環緩沖區是一種常見的解決方案。使用DMA可以自動地將新數據寫入循環緩沖區的末尾,并將舊數據從緩沖區的開頭移出,從而實現無縫的數據傳輸和處理。
(2)每個DMA控制器有8個數據流,每個數據流擁有8個通道,每次傳輸的數據最大為65535(如果數據單位為字,則一次可以傳輸256KB),每個通道具體是什么數據接口如下圖所示:
DMA1通道
DMA2通道
(3)DMA傳輸具有FIFO(先進先出)模式和直接模式。FIFO用于在源數據傳輸到目標地址之前臨時存放這些數據,每個數據流都有一個獨立的 4 字 FIFO,閾值級別可由軟件配置為 1/4、1/2、3/4 或滿。而直接模式在每個外設請求都立即啟動對存儲器傳輸。
(4)數據流與數據流之間的傳輸優先級通過軟件和硬件兩個方面決定。軟件上,可以通過DMA_SxCR寄存器配置數據流的優先級,總共有四個等級:非常高、高、中、低。硬件上,若兩個請求的軟件優先級相同時,仲裁器根據DMA通道的編號來決定響應順序,序號較小的數據流優先級更高,另外,DMA1擁有比DMA2更高的優先級。
(5)DMA支持源地址和目標地址的數據寬度可以不同,如源數據是源源不斷的字節數據,而目標地址要求輸出字寬度的數據。源傳輸和目標傳輸在整個 4 GB 區域(地址在 0x0000 0000 和 0xFFFF FFFF 之間)都可以尋址外設和存儲器。
(6)DMA擁有5個中斷,分別是:半傳輸、傳輸完成、傳輸錯誤、FIFO 上溢/下溢、直接模式錯誤。
2.DMA的使用
下面我們將一些數據通過DMA2的UART1輸出出來:
(1)使能DMA時鐘
?__HAL_RCC_DMA2_CLK_ENABLE();//DMA2時鐘使能?? ?
(2)?初始化DMA通道,配置通道,外設和內存地址,傳輸數據量等
__HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler); //將DMA與USART1聯系起來(發送DMA)//Tx DMA配置UART1TxDMA_Handler.Instance=DMA2_Stream7; //數據流選擇DMA2控制器的數據流7UART1TxDMA_Handler.Init.Channel=DMA_CHANNEL_4; //通道選擇4UART1TxDMA_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; //中等優先級UART1TxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE; UART1TxDMA_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL; UART1TxDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE; //存儲器突發單次傳輸UART1TxDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE; //外設突發單次傳輸HAL_DMA_DeInit(&UART1TxDMA_Handler); HAL_DMA_Init(&UART1TxDMA_Handler);
(3)開啟DMA通道傳輸
MYDMA_USART_Transmit(&UART1_Handler,(u8*)SendBuff,SEND_BUF_SIZE); //啟動傳輸
(4)?查詢DMA通道狀態,傳輸結束后關閉串口DMA
while(1){if(__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TCIF3_7))//等待DMA2_Steam7傳輸完成{__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TCIF3_7);//清除DMA2_Steam7傳輸完成標志HAL_UART_DMAStop(&UART1_Handler); //傳輸完成以后關閉串口DMAbreak; }delayms(10);
}