STM32F0單片機快速入門:聊聊Coolie DMA
1.苦力 DMA
世上本沒有路,走的人多了,便成了路。世上本沒有 DMA,需要搬運的數(shù)據(jù)多了,便有了 DMA。大多數(shù)同學應該沒有在項目中用過這個東西,因為一般情況下也真不需要這個東西。在早期的單片機中也不存在DMA模塊。再加上很多談 DMA 的文章,一上來就先來一個總線架構圖,然后來一大堆讓人生畏的詞兒:共享總線,仲裁器,指針增量,對齊,中斷 ... 好吧,每一個詞都能嚇跑一批膽小的。真的需要這么復雜嗎?就好比我們學開車一樣,能不能先別去嘗試弄懂發(fā)動機的原理,直接掛檔踩油門走起來呢?DMA是很簡單的一個模塊,首先他的功能單一,就是把數(shù)據(jù)從一個地方搬運到另一個地方,再一個它的用法也很簡單,我們還是先從一個例子說起:
我們用 Keil 打開下面這個工程:
STM32Cube_FW_F0_V1.11.0ProjectsSTM32F030R8-NucleoExamplesDMADMA_FLASHToRAMMDK-ARMProject.uvprojx
如圖,有一些存儲在 Flash 的數(shù)據(jù)需要搬運到 RAM 區(qū)的一個數(shù)組。通常我們可以用如下的代碼實現(xiàn):
for(i=0;i<buffer_size;i++)
aDST_Buffer[i] = aSRC_Const_Buffer[i];
上面這個操作是 CPU 親自完成的,首先把數(shù)據(jù)裝進自己的寄存器,再把寄存器中的數(shù)據(jù)存放到目的地址。在例中所示這種數(shù)據(jù)比較少的情況下,這種搬運工作可以說瞬間就完成了。但如果數(shù)據(jù)量比較大,比如說要往顯示屏刷新顯示數(shù)據(jù),就要占用 CPU 大量的時間了。這時候 CPU 就可以叫來 DMA 來干這件苦差事。DMA 就是芯片中的苦力集中營。跟苦力需要交代清楚的最基本的事情就是:從哪兒搬到哪兒,貨物有多少,搬一次還是有貨物源源不斷的到來,需要循環(huán)不斷的搬。讓我們看一下代碼,主程序非常簡單,調(diào)用 DMA_Config(); 進行了一下配置后就自己該干嘛干嘛去了。2.代碼
像串口工程代碼聲明了串口類型的 Handle一樣,這里聲明了一個 DMA 類型的 Handle 來負責 DMA 模塊的處理。
DMA_HandleTypeDef DmaHandle;
需要注意的地方:
__HAL_RCC_DMA1_CLK_ENABLE();
使能模塊時鐘,使能模塊時鐘,使能模塊時鐘!重要的事情要說3遍。在使用任何一個模塊之前首先要使能該模塊的時鐘,這是經(jīng)常被忘記的一件事兒。這個功能在老型號單片機里是沒有的。在不使用某模塊時,徹底關斷其時鐘可以達到最大節(jié)省功耗的目的。
初始化參數(shù)(DmaHandle.Init.):
Direction 從外設到內(nèi)存,從內(nèi)存到內(nèi)存,還是從內(nèi)存到外設?PeriphInc 每傳完一個數(shù)后外設地址是否自增1MemInc 每傳完一個數(shù)后內(nèi)存地址是否自增1PeriphDataAlignment 外設地址對齊方式,Byte,Halfword or WordMemDataAlignment 內(nèi)存地址對齊方式,Byte,Halfword or WordMode 單次,還是循環(huán)模式Priority 優(yōu)先級初始化參數(shù)(DmaHandle.Instance):
DMA模塊中有多個通道,此參數(shù)指明使用哪一個通道。
這個代碼調(diào)用 HAL_DMA_Start_IT 這個函數(shù)啟動了 DMA 傳輸,當數(shù)據(jù)搬運完后會產(chǎn)生一個完成中斷,并調(diào)用回調(diào)函數(shù) TransferComplete。在HAL層驅(qū)動中,已經(jīng)完成了 DMA 中斷所要做的基本處理,比如根據(jù)中斷類型清除相應中斷標志等。在回調(diào)函數(shù)中用戶可以什么都不做,也可以根據(jù)需要添加代碼,比如此例中用點亮 LED 燈的方式來標志傳輸完成。
3.串口如何使用 DMA 傳輸前面的例子是用軟件的方式觸發(fā) DMA 傳輸,在應用中經(jīng)常會用到由某個事件觸發(fā)的情況。比如通過串口發(fā)送,接收中斷來觸發(fā) DMA 傳輸。
我們打開下面這個例子:
STM32Cube_FW_F0_V1.11.0ProjectsSTM32F030R8-NucleoExamplesUARTUART_TwoBoards_ComDMAMDK-ARM Project.uvprojx
在串口初始化的回調(diào)函數(shù) HAL_UART_MspInit(UART_HandleTypeDef *huart)中:
a 聲明了兩個 DMA 類型的 Handle: hdmatx 和 hdmarxb 初始化這兩個 Handlec 把這兩個 Handle 和串口的 UartHandle 連接起來__HAL_LINKDMA(huart, hdmatx, hdma_tx);
__HAL_LINKDMA(huart, hdmarx, hdma_rx);
在串口及其關聯(lián) DMA 通道初始化完成后,既可以啟動DMA方式的接收和發(fā)送。從下圖中可以看到接收 HAL_UART_Receive_DMA 的調(diào)用過程,發(fā)送調(diào)用過程類似:
下圖是UART中斷,和DMA中斷的觸發(fā)調(diào)用過程。USART1模塊產(chǎn)生錯誤時仍然進USART1的中斷向量,DMA模塊傳輸完成或傳輸過程中產(chǎn)生錯誤時進 DMA 中斷向量。
如果沒有迫切的需要,DMA 模塊了解一下就行了。沒有必要在細節(jié)上過多糾纏,即使現(xiàn)在搞懂了,過兩三個月估計也忘了。建議在真正用到大量數(shù)據(jù)傳輸時再仔細研究和優(yōu)化相關代碼。
參考資料:
PM0215 STM32F0xxx Cortex-M0 programming manualUM1785 Description of STM32F0 HAL and low-layer driversSTM32F030 Datasheet

請輸入評論內(nèi)容...
請輸入評論/評論長度6~500個字