什么是DMA,DMA 流如何控制 ?
1.什么是DMA,有什么作用?
因此:轉(zhuǎn)移數(shù)據(jù)(尤其是轉(zhuǎn)移大量數(shù)據(jù))是可以不需要CPU參與。比如希望外設A的數(shù)據(jù)拷貝到外設B,
只要給兩種外設提供一條數(shù)據(jù)通路,直接讓數(shù)據(jù)由A拷貝到B 不經(jīng)過CPU的處理
DMA就是基于以上設想設計的,它的作用就是解決大量數(shù)據(jù)轉(zhuǎn)移過度消耗CPU資源的問題。有了DMA使CPU更專注于更加實用的操作–計算、控制等。
DMA用于在外設與存儲器之間以及存儲器與存儲器之間提供高速數(shù)據(jù)傳輸??梢栽跓o需任何 CPU 操作的情況下通過 DMA 快速移動數(shù)據(jù)。這樣節(jié)省的 CPU 資源可供其它操作使用。
我們用一個現(xiàn)實例子來做個類比。有一家冶煉公司,每天要不斷的從礦場拉礦石進入廠區(qū)冶煉,以前通過公路運送的,但是這個公路除了他家的貨運車以外,還有其他車,所以一般堵車嚴重,交通事故頻發(fā),影響工廠生產(chǎn),貨運成為該公司提高產(chǎn)能的一大瓶頸。公司思量再三,決定出資在礦場和公司間修建一條專用鐵路來運礦石。雖然修建這條鐵路價格昂貴,但是修好后,運送礦石的效率大大提高,公司迅速擴大產(chǎn)能,很快得到回報。同時原來運送礦石這條公路,也不堵車了。這條鐵路的作用其實和DMA類似。
本文通過STM32F4對DMA相關原理做個介紹。
STM32F4共有2個 DMA 控制器,每個控制器均有8個數(shù)據(jù)流,每一個 DMA 控制器都用于管理一個或多個外設的存儲器訪問請求。每個數(shù)據(jù)流總共可以有多達 8 個通道(或稱請求)。每個通道都有一個仲裁器,用于處理 DMA 請求間的優(yōu)先級。
2.DMA傳輸過程簡述
DMA傳輸過程說起來很簡單,每個DMA有2個端口:外設端口和存儲器端口,通過這兩個端口可以單向傳輸數(shù)據(jù)。
DMA1外設端口通過AHB總線連接到外設,存儲器端口通過AHB總線連接到存儲器, DMA1傳輸方向有二種:從外設向存儲器傳輸數(shù)據(jù),從存儲器向外設傳輸數(shù)據(jù)。
DMA2外設端口通過AHB總線連接到外設以及存儲器,存儲器端口通過AHB總線連接到存儲器。DMA2傳輸方向有三種:從外設向存儲器傳輸數(shù)據(jù),從存儲器向外設傳輸數(shù)據(jù),存儲器向存儲器傳輸數(shù)據(jù)。
ARM Cortex-M 處理器的存儲系統(tǒng)使用32位尋址,共有4GB地址空間。ROM、RAM、外設以及處理器內(nèi)的調(diào)試支持部件的地址均映射在這4GB存儲空間內(nèi)。所以不論哪種傳輸方向,DMA數(shù)據(jù)傳輸?shù)谋举|(zhì)是將數(shù)據(jù)從4GB空間內(nèi)的一個地址傳輸?shù)搅硪粋€地址上。
兩個DMA各有1個4字(1字=4字節(jié))大小的緩沖區(qū)(FIFO),用于數(shù)據(jù)在傳輸?shù)侥繕酥?,臨時存儲這些數(shù)據(jù)。緩沖區(qū)有一個可配置的閾值(可配置為1/4、1/2、3/4 或滿),用來決定何時將緩沖區(qū)(FIFO)中數(shù)據(jù)發(fā)送到存儲器(當從外設向存儲器傳輸數(shù)據(jù))或何時將存儲器內(nèi)的數(shù)據(jù)發(fā)送到緩沖區(qū)(FIFO)(從存儲器向外設傳輸數(shù)據(jù))。當從外設向存儲器傳輸數(shù)據(jù)時,緩沖區(qū)(FIFO)內(nèi)數(shù)據(jù)量達到閾值時,會將緩沖區(qū)(FIFO)數(shù)據(jù)發(fā)送到目標地址上。當從存儲器向外設傳輸數(shù)據(jù)時,緩沖區(qū)內(nèi)的數(shù)據(jù)量小于等于緩沖區(qū)(FIFO)閾值時,會使用存儲器內(nèi)的數(shù)據(jù)填充滿FIFO。
通過DMA傳輸數(shù)據(jù),需要以下信息:
DMA外設端口和存儲器端口的數(shù)據(jù)寬度。所謂數(shù)據(jù)寬度是指從端口發(fā)送或從端口接收的數(shù)據(jù)是多少位的。我們在編寫程序時,有時候有這樣的需求(特別是在外設與存儲器間傳輸數(shù)據(jù)),從一個地址將A個N位的數(shù)據(jù)傳輸?shù)搅硪粋€地址,重合組合成B個M位的數(shù)據(jù)。用DMA傳輸數(shù)據(jù)時,通過配置其外設端口和存儲器端口的數(shù)據(jù)寬度可完美解決此問題。DMA中數(shù)據(jù)寬度可配置為字節(jié)、半字(2字節(jié))和字(4字節(jié))。通過DMA_SxCR寄存器的 PSIZE(配置外設端口數(shù)據(jù)寬度) 和 MSIZE(配置內(nèi)存端口數(shù)據(jù)寬度) 位配置。傳輸?shù)臄?shù)據(jù)量。所謂數(shù)據(jù)量是指具體要傳輸多少個數(shù)據(jù)。有兩種控制傳輸數(shù)據(jù)量的方法:(1)連接外設并且要傳輸?shù)臄?shù)據(jù)數(shù)據(jù)量未知時,可通過外設發(fā)送數(shù)據(jù)傳輸?shù)慕Y束信號停止傳輸,但此種方法需要外設具有SDIO接口。(2) 在已知需要傳輸?shù)臄?shù)據(jù)量時,通過設置需要傳輸?shù)臄?shù)據(jù)量。要傳輸?shù)臄?shù)據(jù)量在使能DMA數(shù)據(jù)流之前寫入DMA_SxNDTR 寄存器。在兩邊數(shù)據(jù)寬度不一致情況下,DMA_SxNDTR中的數(shù)據(jù)量等于外設端的數(shù)據(jù)量。每傳輸一次后,DMA_SxNDTR中值遞減1,遞減為0時,DMA停止傳輸。通常情況下,使用第2中方法來控制數(shù)據(jù)量。數(shù)據(jù)傳輸?shù)刂?。所謂數(shù)據(jù)傳輸?shù)刂肥侵笍氖裁吹刂钒l(fā)送數(shù)據(jù),從什么地址接收數(shù)據(jù)。外設端口和存儲器端口各自有自己的數(shù)據(jù)傳輸?shù)刂罚謩e通過寄存器DMA_SxPAR 和DMA_SxM0AR/ DMA_SxM1A(DMA_SxM1AR僅在雙緩沖區(qū)模式下有效)來設置數(shù)據(jù)傳輸?shù)刂贰?.1.DMA普通傳輸過程
DMA有一個使能信號,通過使能信號來控制DMA工作,在使用DMA時,首先需要使能DMA。
針對于不同的傳輸方向,其傳輸過程略有不同。
從外設向存儲器傳輸數(shù)據(jù)
DMA使能后,等待外設發(fā)出DMA請求信號,DMA收到請求,數(shù)據(jù)從外設端口數(shù)據(jù)傳輸?shù)刂?DMA_SxPAR)存儲到DMA緩沖區(qū)(FIFO)內(nèi),當緩沖區(qū)(FIFO)內(nèi)數(shù)據(jù)量達到緩沖區(qū)(FIFO)閾值后,緩沖區(qū)(FIFO)內(nèi)數(shù)據(jù)移出到內(nèi)存端口數(shù)據(jù)傳輸?shù)刂?DMA_SxM0AR)指定的存儲器地址中,同時每傳輸一個數(shù)據(jù),DMA_SxNDTR內(nèi)的值會減1,DMA_SxNDTR值遞減為0,DMA傳輸停止。從存儲器向外設傳輸數(shù)據(jù)
DMA使能后,立即會將內(nèi)存端口數(shù)據(jù)傳輸?shù)刂?DMA_SxM0AR)指定的數(shù)據(jù)移動到DMA緩沖區(qū)(FIFO)內(nèi),完全填滿DMA緩沖區(qū)(FIFO),等待外設端口DMA請求信號,每次發(fā)生外設請求,緩沖區(qū)內(nèi)的數(shù)據(jù)以外設端口寬度指定的數(shù)據(jù)量移出到外設端數(shù)據(jù)傳輸?shù)刂?DMA_SxPAR)指定的目標地址上,同時每傳輸一個數(shù)據(jù),DMA_SxNDTR內(nèi)的值會減1,DMA_SxNDTR值遞減為0,DMA停止傳輸。當DMA緩沖區(qū)(FIFO)內(nèi)數(shù)據(jù)量小于等于緩沖區(qū)閾值時,會使用存儲器內(nèi)的數(shù)據(jù)再次填充滿FIFO。2.2.DMA指針遞增傳輸過程
DMA的外設端口和內(nèi)存端口各自有一個指針遞增參數(shù),可以單獨設置是否啟用。如果啟用了指針遞增,則每傳輸一次,相應端口的數(shù)據(jù)傳輸?shù)刂愤f增(遞增數(shù)等于相應端口地址寬度的字節(jié)數(shù)),指向下一個地址,下次DMA傳輸會使用這個新地址來發(fā)送或接收數(shù)據(jù)。
2.3.DMA循環(huán)傳輸過程
DMA還有一個循環(huán)模式參數(shù)用來設置是否啟用循環(huán)模式。在循環(huán)模式下,當DMA_SxNDTR遞減為0后,外設端口、存儲器端口的數(shù)據(jù)傳輸?shù)刂芬约癉MA_SxNDTR中的值會自動加載初始值后,開始新一輪的循環(huán)傳輸。
2.4.DMA雙緩沖區(qū)傳輸過程
DMA在循環(huán)模式下,還可以設置為雙緩沖區(qū)模式。在雙緩沖區(qū)模式下,存儲器端口可以利用DMA_SxM0AR和 DMA_SxM1AR 2個寄存器設置兩個數(shù)據(jù)傳輸?shù)刂?。DMA可以利用兩個數(shù)據(jù)傳輸?shù)刂方惶鎮(zhèn)鬏敂?shù)據(jù)。我們使能指針遞增情況下,將兩個緩沖區(qū)首地址分別裝入DMA_SxM0AR和 DMA_SxM1AR,這樣在一個緩沖區(qū)傳輸數(shù)據(jù)的時候,可以對另一個緩沖區(qū)內(nèi)的數(shù)據(jù)進行處理。
以上只是粗略簡述整個傳輸過程,具體詳細信息,后面具體描述。
3.STM32F4 DMA的主要特性
雙 AHB 主總線架構,一個用于存儲器訪問,另一個用于外設訪問。每個 DMA 控制器有 8 個數(shù)據(jù)流,每個數(shù)據(jù)流有多達8個通道(或稱請求)每個數(shù)據(jù)流有單獨的四級32位先進先出存儲器緩沖區(qū) (FIFO),可用于 FIFO 模式或直接模式:
—— FIFO 模式下,可通過軟件將閾值級別選取為 FIFO 大小的 1/4、1/2 或 3/4。當FIFO中的數(shù)據(jù)量達到FIFO閾值時,才會將FIFO中的數(shù)據(jù)送出FIFO,進入外設或儲存器端口。
—— 直接模式下,F(xiàn)IFO失去作用,不對數(shù)據(jù)進行緩存,每個 DMA 請求會立即啟動對數(shù)據(jù)的傳輸。當在直接模式(禁止 FIFO)下將 DMA請求配置為以存儲器向外設傳輸數(shù)據(jù)時,DMA 僅會將一個數(shù)據(jù)從存儲器預加載到內(nèi)部 FIFO,從而確保一旦外設觸發(fā) DMA 請求時則立即傳輸數(shù)據(jù)。通過硬件可以將每個數(shù)據(jù)流配置為:
—— 支持外設到存儲器、存儲器到外設和存儲器到存儲器傳輸?shù)某R?guī)通道
—— 也支持在存儲器方雙緩沖的雙緩沖區(qū)通道DMA 數(shù)據(jù)流請求之間的優(yōu)先級可用軟件編程(4 個級別:非常高、高、中、低),在軟件優(yōu)先級相同的情況下可以通過硬件決定優(yōu)先級(例如,請求 0 的優(yōu)先級高于請求 1)要傳輸?shù)臄?shù)據(jù)項的數(shù)目可以由 DMA 控制器或外設管理:
—— DMA 流控制器下,要傳輸?shù)臄?shù)據(jù)項的數(shù)目是 1 到 65535,可用軟件編程
—— 外設流控制器下,要傳輸?shù)臄?shù)據(jù)項的數(shù)目未知并由源或目標外設控制,外設通過硬件發(fā)出傳輸結束的信號獨立的源和目標傳輸寬度(字節(jié)、半字、字):源和目標的數(shù)據(jù)寬度不相等時,DMA 自動封裝/解封必要的傳輸數(shù)據(jù)來優(yōu)化帶寬。這個特性僅在 FIFO 模式下可用,在直接模式下,數(shù)據(jù)寬度必須相等。對源和目標的增量或非增量尋址支持 4 個、8 個和 16 個節(jié)拍的增量突發(fā)傳輸。突發(fā)增量的大小可由軟件配置,通常等于外設 FIFO 大小的一半每個數(shù)據(jù)流都支持循環(huán)緩沖區(qū)管理 5 個事件標志(DMA 半傳輸、DMA 傳輸完成、DMA 傳輸錯誤、DMA FIFO 錯誤、直接模式錯誤),進行邏輯或運算,從而產(chǎn)生每個數(shù)據(jù)流的單個中斷請求4.DMA功能說明
每個DMA均有兩個傳輸端口:存儲器傳輸端口和外設傳輸端口,這兩個端口間可以單向傳輸數(shù)據(jù),任何一方均可作為發(fā)送方,另一方作為接收方。DMA2的外設端口還可連接存儲器,實現(xiàn)存儲器到存儲器的傳輸。
DMA_SxCR 寄存器中的 DIR[1:0]控制傳輸方向。
源傳輸和目標傳輸在整個 4 GB 區(qū)域(地址在 0x0000 0000 和 0xFFFF FFFF 之間)都可以尋址外設和存儲器。
源和目標端口數(shù)據(jù)寬度可配置。通過DMA_SxCR 寄存器的 PSIZE 和 MSIZE 位可分別控制外設端口和存儲器端口寬度,可配置為字節(jié)、半字(2字節(jié))和字(4字節(jié))。當數(shù)據(jù)寬度分別是半字或字時,寫入DMA_SxPAR 或 DMA_SxM0AR/M1AR 寄存器的外設或存儲器地址必須分別在字或半字地址邊界對齊。例如當數(shù)據(jù)寬度為字時,寫入 DMA_SxPAR 或 DMA_SxM0AR/M1AR地址必須是4的倍數(shù)。
DMA_SxPAR (外設地址配置寄存器)或 DMA_SxM0AR(內(nèi)存地址配置寄存器0)/ DMA_SxM1AR(內(nèi)存地址寄存器1——只在雙緩沖區(qū)模式下有效)用來存放外設或內(nèi)存數(shù)據(jù)傳輸?shù)刂贰MA_SxPAR 寄存器中設置外設端口寄存器地址,外設事件發(fā)生后,數(shù)據(jù)會從此地址移動到外設端口或從外設端口移動到此地址。在 DMA_SxMA0R 寄存器(在雙緩沖區(qū)模式的情況下還有 DMA_SxMA1R 寄存器)中設置存儲器地址。外設事件發(fā)生后,將從此存儲器讀取數(shù)據(jù)或?qū)?shù)據(jù)寫入此存儲器。
4.3.通道選擇——DMA_SxCR 寄存器中的 CHSEL[2:0]控制
每個數(shù)據(jù)流都與1個 DMA 請求相關聯(lián),此 DMA 請求可以從 8 個可能的通道請求中選出。
來自外設的 8 個請求(TIM、ADC、SPI、I2C 等)獨立連接到每個通道,具體的連接取決于產(chǎn)品實現(xiàn)情況。
選擇DMA1的數(shù)據(jù)流6的通道5,則意味著UART8_RX外設請求。
如果我們需要通過TIM3_CH2通道發(fā)出DMA請求,則應選擇數(shù)據(jù)流5的通道5。則在TIM2定時器中使能捕獲/比較DMA請求使能(寄存器TIM2_DIER中斷使能寄位CC2DE置1),則在發(fā)生輸入捕獲事件或者輸出比較事件時,會通過DMA1數(shù)據(jù)流5通道5發(fā)出請求DMA請求。
DMA的使用也沒有那么邪乎,很符合大家常規(guī)的一個思考?;究梢詽饪s為當發(fā)生一個啥事的時候,需要某個人A給另一個人B捎個口信,讓B知道是啥信息。套用個場景就是狗蛋來到旺財家門口讓旺財媽知會一下,想和旺財一起出門玩(事件發(fā)生:狗蛋來找旺財玩),旺財媽媽朝屋里大喊一聲:“旺財狗蛋喊你出門玩嘞!”(信息直接傳遞),然后旺財就知道了狗蛋來找他玩這件事。DMA的數(shù)據(jù)傳輸長度類似就是旺財媽那句話的斷句,她可以一口氣直接喊:“旺財狗蛋喊你出門玩嘞“,也可以喊:“旺財!狗蛋~ 喊你!出門~玩嘞!“這樣兩個字兩個字的喊,雖然話是兩個字的往外蹦,但是整體意思也是完整傳達到了。那么這個說法可以套用到一個UART接收DMA傳輸,設置好事件條件是UART收到數(shù)據(jù),那么就把UART外設數(shù)據(jù)寄存器(收到的數(shù)據(jù))按照一個byte的長度直接發(fā)送到事先指定的RAM內(nèi)存位置。
綜上可以看出DMA的使用配置最重要的內(nèi)容就是觸發(fā)條件,然后誰把信息告訴誰,每次傳遞多少數(shù)據(jù)。如下的范例代碼,是配置了ADC每次完成采樣后,能夠?qū)⒉蓸拥降臄?shù)據(jù)依次偏移放置在制定的數(shù)組種。
下面的代碼主要關注的是結構體 DMA_InitStructure的配置。如下代碼,可以看到觸發(fā)條件是完成ADC采樣后,ADC數(shù)據(jù)寄存器中的值就會直接通過DMA更新至指定的數(shù)組中,并且會自動完成數(shù)組中的指針偏移。在實際的使用中就能達到一次配置后,存放ADC數(shù)據(jù)的數(shù)組就會自動一直在刷新對應通道的ADC數(shù)值了。