本文是根據STM32F207的用戶手冊翻譯整理而來git
DMA(Direct memory access)直接內存訪問,被用於內存和內存之間或內存和外設之間的高速數據傳輸。數據傳輸能夠在沒有CPU的干預下快速移動,這樣能夠保持CPU資源處理其餘事情github
DMA 控制器基於複雜的總線矩陣架構,將功能強大的雙 AHB 主總線架構與獨立的 FIFO 結編程
合在一塊兒,優化了系統帶寬,下圖①處,能夠看出雙 AHB 主總線架構與獨立的 FIFO的結構架構
注意看英文備註:app
DMA1控制器AHB外設端口沒有像DMA2同樣鏈接到總線矩陣,因此只有DMA2數據流能夠執行存儲器到存儲器的傳輸優化
咱們對上圖的②處,(DMA1和DMA2結構同樣,咱們就選擇DMA1)詳細看ui
①每一個數據流總共能夠有多達 8 個通道(或稱請求)spa
②DMA1共有8個數據數據流(兩個DMA共有16個數據流)翻譯
③每一個DMA都有數據流仲裁器,用於處理 DMA 請求間的優先級3d
④DMA的數據流又有獨立的FIFO
⑤DMA採用雙 AHB 主總線架構
備註:
①處是選擇器,配置完成只能選擇一個通道,而③處是仲裁器,也就是說,配置完成,可能8個數據流所有存在,由仲裁器判斷優先級
DMA從傳輸事務包含一系列的給定數目的數據傳輸序列。傳輸的數目能夠經過軟件編程,8位,16位或32位。
每一次DMA傳輸包含3個操做
在產生事件後,外設會向 DMA 控制器發送請求信號。 DMA 控制器根據通道優先級處理該請求。只要 DMA 控制器訪問外設, DMA 控制器就會向外設發送確認信號。外設得到 DMA 控制器的確認信號後,便會當即釋放其請求。一旦外設使請求失效, DMA 控制器就會釋放確認信號。若是有更多請求,外設能夠啓動下一個事務
每一個數據流能夠有8個通道
經過上圖①能夠看出,通道選擇仲裁器能夠經過DMA_SxCR寄存器的CHSEL[2:0]配置
DMA的請求能夠來自TIM,ADC,SPI等外設
DMA1的請求通道
DMA2的請求通道
仲裁器爲兩個 AHB 主端口(存儲器和外設端口)提供基於請求優先級的 8 個 DMA 數據流請求管理,並啓動外設/存儲器訪問序列。
優先級管理分爲兩個階段
8個DMA控制器數據流都可以提供源和目標之間的單向傳輸鏈路
每一個數據流配置後均可以執行
要傳輸的數據量(多達 65535)能夠編程,並與鏈接到外設 AHB 端口的外設(請求 DMA 傳輸)的源寬度相關。每一個事務完成後,包含要傳輸的數據項總量的寄存器都會遞減。
源地址和目標地址能夠在整個4G地址空間,在0x00000000和0xFFFFFFFF之間
在DNA_SxCR寄存求的DIR[1:0]配置DMA的傳輸方式
源地址和目標地址的關係
當數據寬度是半字或字時,外設地址或存儲器地址必須是半字或字對齊
當配置成外設到存儲器的DMA傳輸模式時,兩種模式
傳輸開始條件,使能數據流(DMA_SxCR 寄存器中的位 EN 置 1),而後外設發出請求,再而後該請求贏得了數據流仲裁,纔會開始傳輸。
傳輸中止條件,下列知足一條便可
FIFO模式
這種模式,只要使能數據流(DMA_SxCR 寄存器中的位 EN 置 1),存儲器數據就會傳輸到FIFO中,發生外設請求,FIFO數據會移出並存儲到目標地址。當FIFO小於閾值,存儲器的數據會重載FIFO。
直連模式
使能數據流時,DMA傳輸存儲器的第一個數據到內部FIFO,發生外設請求時,DMA把預裝在值發送的目標地址,而後進行下一個數據的傳輸。預裝載的數據大小爲 DMA_SxCR 寄存器中 PSIZE 位字段的值
傳輸中止條件,下列知足一條便可
存儲器到外設模式和外設到存儲器模式同樣,一樣須要對應數據流贏得仲裁,纔會啓動傳輸
這種模式較爲簡單,沒有外設請求
啓動傳輸
DMA_SxCR 寄存器中的使能位 (EN) 置 1 來使能數據流時,數據就會從源地址傳輸到FIFO,到達FIFO閾值時,FIFO數據移出到目標地址
中止傳輸,下列知足一條便可
固然,一樣該數據流須要贏得仲裁
外設和存儲器指針在每次傳輸後自動向後遞增或保持常量,根據DMA_SxCR寄存器的PINC和MINC位。
禁止遞增模式時很是有用的,當外設源和目標數據是經過單個寄存器訪問的
若是使能了遞增模式,則根據在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中編程的數據寬度,下一次傳輸的地址將是前一次傳輸的地址遞增 1(對於字節)、 2(對於半字)或 4(對於字)
爲了優化封裝操做,能夠無論 AHB 外設端口上傳輸的數據的大小,將外設地址的增量偏移大小固定下來。 DMA_SxCR 寄存器中的 PINCOS 位用於將增量偏移大小與外設 AHB 端口或32 位地址(此時地址遞增 4)上的數據大小對齊。 PINCOS 位僅對 AHB 外設端口有影響。
若是將 PINCOS 位置 1,則不論 PSIZE 值是多少,下一次傳輸的地址老是前一次傳輸的地址遞增 4(自動與 32 位地址對齊) 。可是, AHB 存儲器端口不受此操做影響。
若是 AHB 外設端口或 AHB 存儲器端口分別請求突發事務,爲了知足 AMBA 協議(在固定地址模式下不容許突發事務),則須要將 PINC 或 MINC 位置 1。
循環模式可用於處理循環緩衝區和連續數據流(例如 ADC 掃描模式)。可使用 DMA_SxCR寄存器中的 CIRC 位使能此特性。
當激活循環模式時,要傳輸的數據項的數目在數據流配置階段自動用設置的初始值進行加載,並繼續響應 DMA 請求。
也就是說,好比咱們要從內存中採集 64 個字節發送到串口,若是設置爲重複採集,那麼它會在 64 個字節採集完成以後繼續從內存的第一個地址採集,如此循環。這裏咱們設置爲一次連續採集完成以後不循環。因此設置值爲 DMA_Mode_Normal。在咱們下面的實驗中,若是設置此參數爲循環採集,那麼你會看到串口不停的打印數據,不會中斷,
此模式可用於全部 DMA1 和 DMA2 數據流。
經過將 DMA_SxCR 寄存器中的 DBM 位置 1,便可使能雙緩衝區模式。
除了有兩個存儲器指針以外,雙緩衝區數據流的工做方式與常規(單緩衝區)數據流的同樣。使能雙緩衝區模式時,將自動使能循環模式( DMA_SxCR 中的 CIRC 位的狀態是「無關」),並在每次事務結束時交換存儲器指針。
在此模式下,每次事務結束時, DMA 控制器都從一個存儲器目標交換爲另外一個存儲器目標。這樣,軟件在處理一個存儲器區域的同時, DMA 傳輸還能夠填充/使用第二個存儲器區域
基於DMA雙緩衝模式的的特色,應用中必須開闢兩個存儲區以及存放兩個存儲區首地址的存儲寄存器,DMA_SxM0AR和DMA_SxM1AR。
1:DMA_SxM0AR:指向存儲區0(DMA_Memory_0),單緩衝模式下默認使用該寄存器作存儲區指針。
2:DMA_SxM1AR:指向存儲區1(DMA_Memory_1),僅在DMA雙緩衝模式下才能使用。
3:DMA正在訪問的當前存儲區由DMA_SxCR表示
CT:當前目標
CT = 0:DMA正在訪問存儲區0,CPU能夠訪問存儲區1
CT = 1:DMA正在訪問存儲區1,CPU能夠訪問存儲區0
優勢:
使用DMA雙緩衝傳輸,既能夠減小CPU的負荷,又能最大程度地實現DMA數據傳輸和CPU數據處理互不打擾又互不耽擱,DMA雙緩衝模式的循環特性,使用它對存儲區的空間容量要求也會大大下降。尤爲在大批量數據傳送時,你只需開闢兩個合適大小的存儲區,能知足DMA在切換存儲區時的當前新存儲區空出來就好,並不必定要開闢多大多深的存儲空間,單純一味地加大雙緩衝區的深度並不明顯改善數據傳輸情況
要傳輸的數據項數目必須在使能數據流以前編程到 DMA_SxNDTR(要傳輸數據項數目位,NDT)中,當流控制器是外設且 DMA_SxCR 中的 PFCTRL 位置爲 1 時除外。
當使用內部 FIFO 時,源和目標數據的數據寬度能夠經過 DMA_SxCR 寄存器的 PSIZE 和MSIZE 位(能夠是 八、 16 或32 位)編程
DMA控制器能夠產生一個單次傳輸或者四、8或16節拍的突發傳輸
突發大小經過軟件針對兩個 AHB 端口獨立配置,配置時使用 DMA_SxCR 寄存器中的MBURST[1:0] 和 PBURST[1:0] 位
突發大小指示突發中的節拍數,而不是傳輸的字節數。
爲確保數據一致性,造成突發的每一組傳輸都不可分割:在突發傳輸序列期間, AHB 傳輸會鎖定,而且 AHB 總線矩陣的仲裁器不解除對 DMA 主總線的受權。
根據單次或突發配置的狀況,每一個 DMA 請求在 AHB 外設端口上相應地啓動不一樣數量的傳輸
對於須要配置 MBURST 和 MSIZE 位的 AHB 存儲器端口,必須考慮與上述相同的內容。在直接模式下,數據流只能生成單次傳輸,而 MBURST[1:0] 和 PBURST[1:0] 位由硬件強制配置。
必須選擇地址指針(DMA_SxPAR 或 DMA_SxM0AR 寄存器),以確保一個突發塊內的全部傳輸在等於傳輸大小的地址邊界對齊
選擇突發配置必需要遵照 AHB 協議,即突發傳輸不得越過 1 KB 地址邊界,由於能夠分配給單個從設備的最小地址空間是 1 KB。這意味着突發塊傳輸不該越過 1 KB 地址邊界,不然就會產生一個 AHB 錯誤,而且 DMA 寄存器不會報告這個錯誤。
FIFO結構
FIFO是用來臨時存儲從源地址傳來的數據,在這些數據被髮送到目的地市以前。
每一個數據流都有獨立的4字FIFO,他們能夠被軟件配置爲1/四、1/二、3/4或滿
使用FIFO閾值,必須禁止直接模式
下圖是FIFO結構和數據源、閾值關係的示意圖
FIFO閾值和突發設置
警告被要求,選擇 FIFO 閾值(DMA_SxFCR 寄存器的位 FTH[1:0])和存儲器突發大(DMA_SxCR 寄存器的 MBURST[1:0] 位):FIFO 閾值指向的內容必須與整數個存儲器突發傳輸徹底匹配。若是不是這樣,當使能數據流時將生成一個 FIFO 錯誤( DMA_HISR 或 DMA_LISR寄存器的標誌 FEIFx),而後將自動禁止數據流
FIFO更新
FIFO能夠被更新,當數據流被禁止經過寫入DMA_SxCR寄存器的EN位和當數據被配置成外設到存儲器或存儲區到存儲區模式:若是禁止數據流時仍有某些數據存留在FIFO 中, DMA 控制器會將剩餘的數據繼續傳輸到目標(即便已經有效禁止了數據流)。刷新完成時,會將 DMA_LISR 或 DMA_HISR 寄存器中的傳輸完成狀態位 (TCIFx) 置 1。
在這種狀況下,剩餘數據計數器 DMA_SxNDTR 保持的值指示在目標存儲器現有多少可用數據項。
直接模式
默認狀況下, FIFO 以直接模式操做(將 DMA_SxFCR 中的 DMDIS 位置 1),不使用 FIFO閾值級別。若是在每次 DMA 請求以後,系統須要至/自存儲器的當即和單獨傳輸,這種模式很是有用。
當在直接模式(禁止 FIFO)下將 DMA 配置爲以存儲器到外設模式傳輸數據時, DMA 會將一個數據從存儲器預加載到內部 FIFO,從而確保一旦外設觸發 DMA 請求時則當即傳輸數據
如下各類事件都可以結束傳輸過程,並將 DMA_LISR 或 DMA_HISR 狀態寄存器中的 TCIFx位置 1
能夠隨時暫停 DMA 傳輸以供稍後從新開始;也能夠在 DMA 傳輸結束前明確禁止暫停功能,分兩種狀況
控制要傳輸的數據數目的實體稱爲流控制器。此流控制器使用 DMA_SxCR 寄存器中的PFCTRL 位針對每一個數據流獨立配置
流控制器能夠是:
配置 DMA 數據流 x(其中 x 是數據流編號)時應遵照下面的順序
一旦使能了流,便可響應鏈接到數據流的外設發出的任何 DMA 請求。
一旦在 AHB 目標端口上傳輸了一半數據,傳輸一半標誌 (HTIF) 便會置 1,若是傳輸一半中斷使能位 (HTIE) 置 1,還會生成中斷。傳輸結束時,傳輸完成標誌 (TCIF) 便會置 1,若是傳輸完成中斷使能位 (TCIE) 置 1,還會生成中斷。
對於每一個 DMA 數據流,可在發生如下事件時產生中斷
中斷列表
配置代碼
/* Configure DMA Stream */ DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DST_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory; DMA_InitStructure.DMA_BufferSize = (uint32_t)32; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;. DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure);
設置 DMA 數據流對應的通道,供每一個數據流選擇的通道請求多達 8 個,取值有
#define DMA_Channel_0 ((uint32_t)0x00000000) #define DMA_Channel_1 ((uint32_t)0x02000000) #define DMA_Channel_2 ((uint32_t)0x04000000) #define DMA_Channel_3 ((uint32_t)0x06000000) #define DMA_Channel_4 ((uint32_t)0x08000000) #define DMA_Channel_5 ((uint32_t)0x0A000000) #define DMA_Channel_6 ((uint32_t)0x0C000000) #define DMA_Channel_7 ((uint32_t)0x0E000000)
DMA 傳輸的外設基地址,假設進行uart1串口DMA 傳輸,咱們能夠按照寄存器的地址偏移直接設置地址:0x40011004,也能夠直接使用ST提供庫的表示方法:&USART1->DR
DMA_Memory0BaseAddr :
DMA 傳輸的內存基地址
DMA_DIR:
設置數據傳輸方向,有存儲器到存儲器,存儲器到外設,外設到存儲器三種選擇,取值有:
#define DMA_DIR_PeripheralToMemory ((uint32_t)0x00000000) #define DMA_DIR_MemoryToPeripheral ((uint32_t)0x00000040) #define DMA_DIR_MemoryToMemory ((uint32_t)0x00000080)
DMA_BufferSize:
設置一次傳輸數據量的大小
DMA_PeripheralInc:
設置傳輸數據的時候外設地址是不變仍是遞增,若是設置爲遞增,那麼下一次傳輸的時候地址加 1,取值有:
#define DMA_PeripheralInc_Enable ((uint32_t)0x00000200) #define DMA_PeripheralInc_Disable ((uint32_t)0x00000000)
DMA_MemoryInc:
設置傳輸數據時 候內存地址 是否遞增。 這個參數和DMA_PeripheralInc 意思接近,只不過針對的是內存(存儲器),取值有:
#define DMA_MemoryInc_Enable ((uint32_t)0x00000400) #define DMA_MemoryInc_Disable ((uint32_t)0x00000000)
DMA_PeripheralDataSize:
設置外設的數據長度是爲字節傳輸(8bits),半字 傳 輸 (16bits) 還 是 字 傳 輸 (32bits),取值有:
#define DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) #define DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000800) #define DMA_PeripheralDataSize_Word ((uint32_t)0x00001000)
DMA_MemoryDataSize:
用來設置內存的數據長度
DMA_Mode:
設置 DMA 模式是否循環採集,取值有:
#define DMA_Mode_Normal ((uint32_t)0x00000000) #define DMA_Mode_Circular ((uint32_t)0x00000100)
DMA_Priority:
設置 DMA 通道的優先級,有低,中,高,超高三種模式。就是仲裁器仲裁的時候用的,當多個數據流同時開啓,DMA仲裁器優先處理優先級高的數據流,取值有:
#define DMA_Priority_Low ((uint32_t)0x00000000) #define DMA_Priority_Medium ((uint32_t)0x00010000) #define DMA_Priority_High ((uint32_t)0x00020000) #define DMA_Priority_VeryHigh ((uint32_t)0x00030000)
DMA_FIFOMode:
設置是否開啓 FIFO 模式,取值有:
#define DMA_FIFOMode_Disable ((uint32_t)0x00000000) #define DMA_FIFOMode_Enable ((uint32_t)0x00000004)
DMA_FIFOThreshold:
選擇 FIFO 閾值,只有上個參數選擇使能FIFO,這個參數纔有用。取值有:
#define DMA_FIFOThreshold_1QuarterFull ((uint32_t)0x00000000) #define DMA_FIFOThreshold_HalfFull ((uint32_t)0x00000001) #define DMA_FIFOThreshold_3QuartersFull ((uint32_t)0x00000002) #define DMA_FIFOThreshold_Full ((uint32_t)0x00000003)
DMA_MemoryBurst:
配置存儲器突發傳輸配置。能夠選擇爲 4 個節拍的增量突發傳輸 ,8 個節拍的增量突發傳輸 , 16 個街拍的增量突發傳輸以及單次傳輸。取值有:
#define DMA_MemoryBurst_Single ((uint32_t)0x00000000) #define DMA_MemoryBurst_INC4 ((uint32_t)0x00800000) #define DMA_MemoryBurst_INC8 ((uint32_t)0x01000000) #define DMA_MemoryBurst_INC16 ((uint32_t)0x01800000)
DMA_PeripheralBurst:
配置外設突發傳輸配置。跟前面一個參數DMA_MemoryBurst 做用相似,只不過一個針對的是存儲器。取值有:
#define DMA_PeripheralBurst_Single ((uint32_t)0x00000000) #define DMA_PeripheralBurst_INC4 ((uint32_t)0x00200000) #define DMA_PeripheralBurst_INC8 ((uint32_t)0x00400000) #define DMA_PeripheralBurst_INC16 ((uint32_t)0x00600000)
開源地址:
https://github.com/strongercjd/STM32F207VCT6
點擊查看本文所在的專輯,STM32F207教程