淺談STM32的DMA模塊的使用

淺談STM32的DMA模塊的使用


                              By Jack Chang


                  Thu Jun 5 2008  18:30:49


                 E-mail:[email protected] QQ:179748613


前言:接觸MCU的編程已經有幾年的歷史,剛開始是從PLC(C語言的)學習與應用,後來有學習了8bits的AVR單片機,對MCU有一定的瞭解;最近接觸了STM32的Chip,發現其優點不只是單單宣傳的32bits的MCU的速度,而且在編程的風格上也有了較大的變化(與我原有的編程習慣);特別是有一個與CPU並行運行的模塊:DMA,對它印象深刻。現在就和大家一起討論學習這個Module,談談我在學習中一些感悟和大家一起分享;如有對DMA理解和應用上的錯誤和偏差,歡迎“拍磚”和提出更正,蝦米在此感謝大家了。哈哈!


什麼是STM32的DMA?其全稱是:Direct Memory Access;根據ST公司提供的相關信息,DMA是STM32中一個獨立與Cortex-M3內核的模塊,有點類似與ADC、PWM、TIMER等模塊;主要功能是通信“橋樑”的作用,可以將所有外設映射的寄存器“連接”起來,這樣就可以高速問各寄存器,其傳輸不受CPU的支配,傳輸還是雙向的;例如,從“表面”上看,它可以將flash中的數據與儲存器中變量建立通訊,還可以將一外設的積存器或緩衝器與另外設的寄存器或緩衝器建立雙向通訊,有點像把外設硬件之間用“導線”連接在一起了。其間的通訊不佔CPU資源,訪問速度高,對於實時性強的應用將是一個很好的選擇;當然,對於實時性非常強的,建議還是採用專用的DSP芯片。


過程:怎樣啓用DMA?首先,衆所周知的是初始化,任何設備啓用前都要對其進行初始化,要對模塊初始化,還要先了解該模塊相應的結構及其函數,以便正確的設置;由於DMA較爲複雜,我就只談談DMA的基本結構和和常用函數,這些都是ST公司提供在庫函數中的。


1、  下面代碼是一個標準DMA設置,當然實際應用中可根據實際情況進行裁減:


 


  DMA_DeInit(DMA_Channel1);


  上面這句是給DMA配置通道,根據ST提供的資料,STM3210Fx中DMA包含7個通道(CH1~CH7),也就是說可以爲外設或memory提供7座“橋樑”(請允許我使用橋樑一詞,我覺得更容易理解,哈哈,別“拍磚”呀!);


 


  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;


  上面語句中的DMA_InitStructure是一個DMA結構體,在庫中有聲明瞭,當然使用時就要先定義了;DMA_PeripheralBaseAddr是該結構體中一個數據成員,給DMA一個起始地址,好比是一個buffer起始地址,數據流程是:外設寄存器à DMA_PeripheralBaseAddàmemory中變量空間(或flash中數據空間等),ADC1_DR_Address是我定義的一個地址變量;


 


  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue;


  上面這句很顯然是DMA要連接在Memory中變量的地址,ADC_ConvertedValue是我自己在memory中定義的一個變量;


 


  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;


  上面的這句是設置DMA的傳輸方向,就如前面我所說的,DMA可以雙向傳輸,也可以單向傳輸,這裏設置的是單向傳輸,如果需要雙向傳輸:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。


 


 


 DMA_InitStructure.DMA_BufferSize = 2;


  上面的這句是設置DMA在傳輸時緩衝區的長度,前面有定義過了buffer的起始地址:ADC1_DR_Address ,爲了安全性和可靠性,一般需要給buffer定義一個儲存片區,這個參數的單位有三種類型:Byte、HalfWord、word,我設置的2個half-word(見下面的設置);32位的MCU中1個half-word佔16 bits。


 


 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;


  上面的這句是設置DMA的外設遞增模式,如果DMA選用的通道(CHx)有多個外設連接,需要使用外設遞增模式:DMA_PeripheralInc_Enable;我的例子裏DMA只與ADC1建立了聯繫,所以選用DMA_PeripheralInc_Disable


 


DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;


上面的這句是設置DMA的內存遞增模式,DMA訪問多個內存參數時,需要使用DMA_MemoryInc_Enable,當DMA只訪問一個內存參數時,可設置成:DMA_MemoryInc_Disable。


 


 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;


上面的這句是設置DMA在訪問時每次操作的數據長度。有三種數據長度類型,前面已經講過了,這裏不在敘述。


 


DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;


與上面雷同。在此不再說明。


 


  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;


  上面的這句是設置DMA的傳輸模式:連續不斷的循環模式,若只想訪問一次後就不要訪問了(或按指令操作來反問,也就是想要它訪問的時候就訪問,不要它訪問的時候就停止),可以設置成通用模式:DMA_Mode_Normal


 


  DMA_InitStructure.DMA_Priority = DMA_Priority_High;


  上面的這句是設置DMA的優先級別:可以分爲4級:VeryHigh,High,Medium,Low.


 


  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;


  上面的這句是設置DMA的2個memory中的變量互相訪問的


 


  DMA_Init(DMA_Channel1,&DMA_InitStructure);


  前面那些都是對DMA結構體成員的設置,在次再統一對DMA整個模塊做一次初始化,使得DMA各成員與上面的參數一致。


 


  /*DMA Enable*/


  DMA_Cmd(DMA_Channel1,ENABLE);


  哈哈哈!這一句我想我就不羅嗦了,大家一看就明白。


 


至此,整個DMA總算設置好了,但是,DMA通道又是怎樣與外設聯繫在一起的呢?哈哈,這也是我當初最想知道的一個事情,別急!容我想喝口茶~~~~~~哈哈哈!


要使DMA與外設建立有效連接,這不是DMA自身的事情,是各個外設的事情,每個外設都有 一個xxx_DMACmd(XXXx,Enable )函數,如果使DMA與ADC建立有效聯繫,就使用ADC_DMACmd(ADC1,Enable); (這裏我啓用了ADC中的ADC1模塊)。


 


下面就以我的一個實例來和大家一起學習。這個實例仿真是成功的,我使用的是IAR Embedded Workbench IDE(v4.42) 和Manley Mini-kit評估板+ST-Link II來調試,是一個USART+ADC+DMA的例子。


首先,按照相關的資料提示,建立好項目路徑、拷貝庫文件和相關必要的文件,並建立一個新工程;還有就是設置好IAR IDE的相關設置。一切準備就緒後就開始修改相關的文檔:


 conf.h文檔的修改是根據應用中有啓用的相關功能Module。如下圖:


ADC模塊的啓用設置:



點擊看大圖 


DMA模塊的啓用設置:



點擊看大圖 


USART模塊啓用的設置:


點擊看大圖


 



其他的如flash、GPIO、RCC、NVIC、HSE等就不一一舉例,因爲這些幾乎每個工程都要用到。


   


再回頭看看我的main.c文檔,


下圖是相關的私有定義


點擊看大圖


 



USART的配製如下(9600,8, N,1):


點擊看大圖


 



DMA的配置如下:



點擊看大圖 


ADC的配置如下圖(採用了2路A/D多通道的ADC模式):



點擊看大圖 


給各個模塊配置的時鐘使能如下圖;在此想羅嗦一句就是在這個位置的設置我沒有給DMA配置時鐘,造成DMA功能無法正常使用,也就是前面所有說的與我原先的編程風格有較大的變化的原因,想說一句:在項目中所使用的所有功能模塊都要在此配置時鐘才能正常工作。



 點擊看大圖


  


GPIO的配置



點擊看大圖 


如果有開中斷子程序,請在在NVIC中配置,還可以定義每個中斷子程序的優先級別,由於我的工程沒有用到,在此就不羅嗦了。


 


我項目應用到的文件目錄如下圖:



 


我的項目仿真結果如下;PA0與GND間接了一個1.5V的乾電池,其結果是如中ADC_ConvertedValue[0]的值;PA1懸空,未接模擬信號。



點擊看大圖 


我的硬件仿真平臺如下2圖:紅色的是我AVR mega16L Mini開發板,主要是利用了其中的POWER和RS232



  點擊看大圖 


    點擊看大圖


 


用PC端USART數據採集結果:(圖中的顯示是我將ADC轉換後的值給字符串化了)


點擊看大圖



ST-link II仿真窗口的測試結果與USART採集到的數據結果差異說明:由於Manley 的stme32 Mini-kit評估板套件無法給ADC和USART同時組合仿真,我是分別分開仿真和USART數據傳輸的,所以出現差異。


 


心得:  1、這次學習STM32的MCU,最大的心得是改變我原有的編程思維,原來學習AVR的時候,文件庫需要自己寫,屬於底層操作,如要自己寫直接操作各外設的寄存器,複雜的還需要自己寫設置函數,對底層要有非常的清楚的認識,編寫的代碼的工作量也比較大;稍有不慎,容易把寄存器弄錯;調試也會花費較大的時間。而STM32的編程風格給我一個全新的視角(哈哈!別拍磚,雖然屬菜鳥級別,但比較容易接受新的東西),感覺只要按其提供的資料建立起工程的基本構架,很多都是ST公司提供封裝好的接口(類似windows的API),直接調用接口和做少許修改就可以完成你想要的工程結果。


2、  清晰的工程構架:ST提供一些工程案例的基本構架,只要熟悉這些構架,對構架做一些適當的修改,你將很輕鬆的完成你的任務,開發週期也可以縮短很多。請記住:牢記工程構架;


3、  哈哈!不想羅嗦的是STM32的速度和豐富的外設。(再羅嗦可能會被成批的磚頭拍死,哈哈哈哈!)


4、  更有信心用STM32做其他更復雜一些的項目。祝大家好運!(別拍磚頭呀!哈哈!)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章