STM32F429入门(十八):DMA
DMA(Direct Memory Access,直接存儲區(qū)訪問)為實現(xiàn)數(shù)據(jù)高速在外設(shè)寄存器與存儲器之 間或者存儲器與存儲器之間傳輸提供了高效的方法。它不占CPU,可以節(jié)省很多資源。有時候當(dāng)我們需要在兩個存儲器之間傳輸數(shù)據(jù)時,我們需要通過CPU,從A傳送到B,也就是說它可以減少CPU的負(fù)擔(dān)。
-
DMA1:P->M,M->P
-
DMA2:P->M,M->P,M->M
(P為外設(shè),M為存儲器)
存儲區(qū)到外設(shè)傳輸就是把特定存儲區(qū)內(nèi)容轉(zhuǎn)移至外設(shè)的數(shù)據(jù)寄存器中,這種多用于外設(shè)的發(fā)送通信。
存儲器到存儲器傳輸就是把一個指定的存儲區(qū)內(nèi)容拷貝到另一個存儲區(qū)空間。
一、DMA功能框圖
?
?(一)通道+流
-
流:是數(shù)據(jù)傳輸?shù)囊粭l鏈路,每個DMA控制器有8條獨(dú)立的數(shù)據(jù)流,每次傳輸?shù)臄?shù)據(jù)最大為65535,如果數(shù)據(jù)的單位為字的話,那一次可以傳輸256KB。(2部分)
-
通道:每個數(shù)據(jù)流有8個通道選擇,每個通道對應(yīng)不同的DMA請求。(1部分)
DMA1請求映射:
?DMA2請求映射:
?具體要怎么選擇,可以使用寄存器:DMA_SxCR:CHSEL
?如果多個DMA請求一起來,如何處理?這個時候就需要仲裁器。
(二)仲裁器
軟件階段:DMA_SxCR:PL
?
?
????????2.硬件階段:數(shù)據(jù)流編號小的優(yōu)先級大
同一個數(shù)據(jù)流只能使用一個通道,同一個DMA控制器可以使用多個數(shù)據(jù)流(需要優(yōu)先級)。
(三)FIFO
-
源和目標(biāo)之間的一個數(shù)據(jù)中轉(zhuǎn)站
-
每個數(shù)據(jù)流都獨(dú)立擁有四級 32 位 FIFO(先進(jìn)先出存儲器緩沖區(qū))。DMA 傳輸具有 FIFO 模式和直接模式。
-
每個數(shù)據(jù)流有4字FIFO,閾值級別有1/4,2/4,3/4,4/4。具體配置的寄存器為DMA_SxFCR:FTH。
-
在開啟FIFO時,直接模式要禁止,MA_SxFCR:DMDIS。
直接模式和FIFO模式有什么區(qū)別呢:
-
直接模式在每個外設(shè)請求都立即啟動對存儲器傳輸。在直接模式下,如果 DMA 配置為存儲器到外設(shè)傳輸那 DMA 會見一個數(shù)據(jù)存放在 FIFO 內(nèi),如果外設(shè)啟動 DMA 傳輸請求就可以馬上將數(shù)據(jù)傳輸過去。它不需要等待是否達(dá)到了所設(shè)定的閾值級別。直接模式要求源地址和目標(biāo)地址的數(shù)據(jù)寬度必須一致,直接模式不能用于存儲器到存儲器傳輸。
-
FIFO 用于在源數(shù)據(jù)傳輸?shù)侥繕?biāo)地址之前臨時存放這些數(shù)據(jù)。可以通過 DMA 數(shù)據(jù)流 xFIFO 控制寄存器 DMA_SxFCR 的 FTH[1:0]位來控制 FIFO 的閾值,分別為 1/4、1/2、3/4 和滿。如果數(shù)據(jù)存儲量達(dá)到閾值級別時,FIFO 內(nèi)容將傳輸?shù)侥繕?biāo)中。
那FIFO有什么優(yōu)勢呢?
FIFO 對于要求源地址和目標(biāo)地址數(shù)據(jù)寬度不同時非常有用,比如源數(shù)據(jù)是源源不斷的字節(jié)數(shù)據(jù),而目標(biāo)地址要求輸出字寬度的數(shù)據(jù),即在實現(xiàn)數(shù)據(jù)傳輸時同時把原來 4 個 8 位 字節(jié)的數(shù)據(jù)拼湊成一個 32 位字?jǐn)?shù)據(jù)。此時使用 FIFO 功能先把數(shù)據(jù)緩存起來,分別根據(jù)需要輸出數(shù)據(jù)。
-
FIFO 另外一個作用使用于突發(fā)(burst)傳輸。(下面詳解)
那FIFO閾值與突發(fā)配置的關(guān)系如下表:
講到這一點(diǎn),我們需要理清計算機(jī)的知識,關(guān)于字和字節(jié)的關(guān)系,我一直懵懵懂懂。
-
位:計算機(jī)處理的最小單元,它不隨CPU的處理能力的變化而變化。
-
字節(jié):就是8位數(shù)據(jù)的大小,它也不隨CPU的處理能力的變化而變化。
-
字:計算機(jī)進(jìn)行數(shù)據(jù)處理時,一次存取、加工、傳送的數(shù)據(jù)長度陳為字(word),一個字通常是由一個或多個字節(jié)(一般是字節(jié)的整數(shù)倍)構(gòu)成。
-
字長:計算機(jī)的每個字所包含的位數(shù)稱為字長。一般我們所說的8位機(jī)、16位機(jī)、32位機(jī)、64位機(jī)指的就是計算機(jī)的字長。
如上的總結(jié)可得如下:
-
在16位CPU中,一個字表示16位(兩個字節(jié))
-
在32位CPU中,一個字表示32位(四個字節(jié)):STM32
-
在64位CPU中,一個字表示64位 (八個字節(jié))
-
...
也就是說,在上面的表中,如果你的閾值級別設(shè)置為1/4,那么它就只能發(fā)一個字節(jié)(四個節(jié)拍),如果是1/2,那么它就只能發(fā)兩個字節(jié),可以為一個字節(jié)兩次突發(fā),或者是兩個字節(jié)一次突發(fā)....
我也很好奇的去了解了FIFO如何設(shè)置以及滿閾值與FIFO深度的關(guān)系:
FIFO閾值包含將滿閾值afull_cnt和將空閾值aempty_cnt,當(dāng)FIFO內(nèi)包含的數(shù)據(jù)data_cnt大于等于afull_cnt時,將滿信號有效(afull為1),afull傳輸給上游模塊A,通知上游模塊停止發(fā)送數(shù)據(jù),防止FIFO發(fā)送溢出,NOTE:將滿閾值afull_cnt的作用是防止FIFO發(fā)送溢出導(dǎo)致數(shù)據(jù)丟失。將空閾值的作用時防止FIFO空的,即FIFO中沒有有效數(shù)據(jù)了還會產(chǎn)生讀數(shù)據(jù)操作。
原文鏈接:FIFO閾值如何設(shè)置?將滿閾值與FIFO深度的關(guān)系?_IC小鴿的博客-CSDN博客
那么如何設(shè)置滿閾值?
1)當(dāng)FIFO中的數(shù)據(jù)為afull_cnt時,產(chǎn)生afull=1。
2)Afull=1經(jīng)過M拍到達(dá)模塊A,此時FIFO中應(yīng)該有(afull_cnt+M)個數(shù)據(jù)。
3)Afull=1到達(dá)模塊A時,模塊A立即停止發(fā)送數(shù)據(jù),此時電路中還存在N拍數(shù)據(jù)將陸續(xù)送到FIFO中,所以最后FIFO中應(yīng)該為(afull_cnt+M+N)個數(shù)據(jù)。
4)為了保證數(shù)據(jù)不會溢出,所以應(yīng)該滿足公式depth_fifo>= afull_cnt+M+N,因此,將滿閾值應(yīng)該至少為depth_fif-(M+N)。
FIFO深度depth應(yīng)該為多少?
若depth_fifo過小,afull有效之后,fifo中存儲的數(shù)據(jù)將很快被下游數(shù)據(jù)讀取,而新的數(shù)據(jù)又無法及時到達(dá)FIFO,因此會造成流水氣泡,影響電路性能。
(四、五、六)M接口、P接口、編程接口
?由圖可得,DMA2可以訪問Flash、SRAM、外設(shè)AHB、APB。它無法實現(xiàn)從外設(shè)到外設(shè)。而DMA1只能訪問APB1的外設(shè)。
DMA 控制器的功能是快速轉(zhuǎn)移內(nèi)存數(shù)據(jù),需要一個連接至源數(shù)據(jù)地址的端口和一個連接至目標(biāo)地址的端口。
-
DMA2(DMA 控制器2)的存儲器端口和外設(shè)端口都是連接到 AHB 總線矩陣,可以使用 AHB 總線矩陣功能。DMA2 存儲器和外設(shè)端口可以訪問相關(guān)的內(nèi)存地址,包括有內(nèi)部 Flash、內(nèi)部 SRAM、AHB1 外設(shè)、AHB2 外設(shè)、APB2 外設(shè)和外部存儲器空間。
-
DMA1 的存儲區(qū)端口相比 DMA2 的要減少 AHB2 外設(shè)的訪問權(quán),同時 DMA1 外設(shè)端 口是沒有連接至總線矩陣的,只有連接到 APB1 外設(shè),所以 DMA1不能實現(xiàn)存儲器到存儲 器傳輸。
編程端口:AHB 從器件編程端口是連接至 AHB2 外設(shè)的。AHB2 外設(shè)在使用 DMA 傳輸時需要相關(guān)控制信號。
二、DMA數(shù)據(jù)配置
DMA工作模式有多樣,具有多種可能的工作模式:
(一)DMA傳輸模式:DMA2三種(外設(shè)到存儲器,存儲器到外設(shè),存儲器到存儲器)、DMA1兩種(外設(shè)到存儲器,存儲器到外設(shè))
模式選擇可以通過 DMA_SxCR 寄存器的 DIR[1:0]位控制,進(jìn)而將 DMA_SxCR 寄存器的 EN 位置 1 就可以使能 DMA傳輸。
在 DMA_SxCR 寄存器的 PSIZE[1:0]和 MSIZE[1:0]位分別指定外設(shè)和存儲器數(shù)據(jù)寬度大小,可以指定為字節(jié)(8 位)、半字(16 位)和字(32 位),我們可以根據(jù)實際情況設(shè)置。直接模式要求外設(shè)和存儲器數(shù)據(jù)寬度大小一樣,實際上在這種模式下 DMA 數(shù)據(jù)流直接使用 PSIZE,MSIZE 不被使用。
(二)源地址和目標(biāo)地址
DMA 數(shù)據(jù)流 x 外設(shè)地址 DMA_SxPAR(x 為 0~7)寄存器用來指定外設(shè)地址,它是一個 32 位數(shù)據(jù)有效寄存器。DMA 數(shù)據(jù)流 x 存儲器 0 地址 DMA_SxM0AR(x 為 0~7) 寄存器和 DMA 數(shù)據(jù)流 x 存儲器 1 地址 DMA_SxM1AR(x 為 0~7) 寄存器用來存放存儲器地址,其中 DMA_SxM1AR 只用于雙緩沖模式,DMA_SxM0AR 和 DMA_SxM1AR 都是 32 位數(shù)據(jù)有效 的。
當(dāng)選擇外設(shè)到存儲器模式時,即設(shè)置 DMA_SxCR 寄存器的 DIR[1:0] 位為“00”, DMA_SxPAR 寄存器為外設(shè)地址,也是傳輸?shù)脑吹刂?#xff0c;DMA_SxM0AR 寄存器為存儲器地址,也是傳輸?shù)哪繕?biāo)地址。對于存儲器到存儲器傳輸模式,即設(shè)置 DIR[1:0] 位為“10”時, 采用與外設(shè)到存儲器模式相同配置。而對于存儲器到外設(shè),即設(shè)置 DIR[1:0]位為“01”時, DMA_SxM0AR 寄存器作為為源地址,DMA_SxPAR 寄存器作為目標(biāo)地址。
?
-
雙緩沖模式
設(shè)置 DMA_SxCR 寄存器的 DBM 位為 1 可啟動雙緩沖傳輸模式,并自動激活循環(huán)模式。(不應(yīng)用于存儲器搭到存儲器的傳輸)
雙緩沖模式下,兩個存儲器地址指針都有效,即 DMA_SxM1AR 寄存器將被激活使用。開始傳輸使用 DMA_SxM0AR 寄存器的地址指針?biāo)鶎?yīng)的存儲區(qū),當(dāng)這個存儲區(qū)數(shù)據(jù)傳輸完 DMA 控制器會自動切換至 DMA_SxM1AR 寄存 器的地址指針?biāo)鶎?yīng)的另一塊存儲區(qū),如果這一塊也傳輸完成就再切換至 DMA_SxM0AR 寄存器的地址指針?biāo)鶎?yīng)的存儲區(qū),這樣循環(huán)調(diào)用。(這樣的傳輸模式適用于傳輸較大信息的情況下)
當(dāng)其中一個存儲區(qū)傳輸完成時都會把傳輸完成中斷標(biāo)志 TCIF 位置 1,如果我們使能了 DMA_SxCR 寄存器的傳輸完成中斷,則可以產(chǎn)生中斷信號。
敲重點(diǎn):!!!
DMA_SxCR 寄存器的 CT 位,當(dāng) DMA 控制器是在訪問使用 DMA_SxM0AR 時 CT=0,此時 CPU 不能訪問 DMA_SxM0AR,但可以向 DMA_SxM1AR 填充或者讀取數(shù)據(jù);當(dāng) DMA 控制器是在訪問使用 DMA_SxM1AR 時 CT=1,此時 CPU 不能訪問 DMA_SxM1AR,但可以向 DMA_SxM0AR 填充或者讀取數(shù)據(jù)。在未使能 DMA 數(shù)據(jù)流傳輸時,可以直接寫 CT 位,改變開始傳輸?shù)哪繕?biāo)存儲區(qū)。
雙緩沖模式應(yīng)用在需要解碼程序的地方是非常有效的。比如MP3,它需要特定的解碼庫程序來解碼文件才能得到可以播放的PCM信號,先讀取一段原始數(shù)據(jù)到緩沖區(qū),然后對緩沖區(qū)的內(nèi)容進(jìn)行解碼,解碼后才可以輸出到音頻播放電路,這個流程對CPU的要求極高,容易出現(xiàn)播放不流暢的現(xiàn)姓。使用雙緩沖就可以解決這個問題,達(dá)到解碼和輸出音頻數(shù)據(jù)到音頻電路同步進(jìn)行的效果。
(三)流控制器
主要是控制DMA傳輸停止的作用。如果我們得知我們需要傳輸多少數(shù)據(jù),那我們可以在傳輸之前設(shè)置DMA_SxNDTR 寄存器為要傳輸數(shù)目值,DMA控制器傳輸完設(shè)置的數(shù)目后,就可以控制DMA停止傳輸。
DMA 數(shù)據(jù)流 x 數(shù)據(jù)項數(shù) DMA_SxNDTR(x 為 0~7)寄存器用來記錄當(dāng)前仍需要傳輸數(shù)目,它是一個 16 位數(shù)據(jù)有效寄存器,即最大值為 65535。在編程時一般都會明確指定一個傳輸數(shù)量,在完成一次數(shù)目傳輸后 DMA_SxNDTR 計數(shù)值就會自減,當(dāng)達(dá)到零時就說明傳輸完成。
如果我們不知道我們需要傳輸多少數(shù)值,那就無法調(diào)用寄存器使其自動控制傳輸,我們可以用外設(shè)通過硬件通信向DMA控制器發(fā)送停止傳輸信號。這里的前提是外設(shè)必須可以發(fā)出這個停止的傳輸信號,只有SDIO才有這個功能。
(四)循環(huán)模式
循環(huán)模式在傳輸一次后會自動按照相同配置重新傳輸,周而復(fù)始直至被控制停止或傳輸發(fā)生錯誤。
使用寄存器:DMA_SxCR 寄存器的 CIRC 位。
(五)傳輸類型
DMA傳輸類型:單次傳輸和突發(fā)傳輸。
-
突發(fā)傳輸:在非常短時間內(nèi)結(jié)合非常高數(shù)據(jù)信號傳輸數(shù)據(jù)。在傳輸階段把速度提高,實現(xiàn)高速傳輸,在數(shù)據(jù)傳輸完成后恢復(fù)正常速度。這個過程要占用AHB總線,保證要求每個數(shù)據(jù)項在傳輸過程中不被分割,這樣一次性把數(shù)據(jù)傳輸完才釋放AHB總線。
-
單次傳輸:必須通過AHB總線仲裁多次控制才傳輸完成。
(六)直接模式
在直接模式下,如果 DMA 配置為存儲器到外設(shè)傳輸那 DMA 會見一個數(shù)據(jù)存放在 FIFO 內(nèi),如果外設(shè)啟動 DMA 傳輸請求就可以馬上將數(shù)據(jù)傳輸過去。
(七)DMA中斷
每個 DMA 數(shù)據(jù)流可以在發(fā)送以下事件時產(chǎn)生中斷:
達(dá)到半傳輸:DMA 數(shù)據(jù)傳輸達(dá)到一半時 HTIF 標(biāo)志位被置 1,如果使能 HTIE 中 斷控制位將產(chǎn)生達(dá)到半傳輸中斷;
傳輸完成:DMA 數(shù)據(jù)傳輸完成時 TCIF 標(biāo)志位被置 1,如果使能 TCIE 中斷控制 位將產(chǎn)生傳輸完成中斷;
傳輸錯誤:DMA 訪問總線發(fā)生錯誤或者在雙緩沖模式下試圖訪問“受限”存儲器 地址寄存器時 TEIF 標(biāo)志位被置 1,如果使能 TEIE 中斷控制位將產(chǎn)生傳輸錯誤中 斷;
FIFO 錯誤:發(fā)生 FIFO 下溢或者上溢時 FEIF 標(biāo)志位被置 1,如果使能 FEIE 中斷 控制位將產(chǎn)生 FIFO 錯誤中斷;
直接模式錯誤:在外設(shè)到存儲器的直接模式下,因為存儲器總線沒得到授權(quán),使 得先前數(shù)據(jù)沒有完成被傳輸?shù)酱鎯ζ骺臻g上,此時 DMEIF 標(biāo)志位被置 1,如果使 能 DMEIE 中斷控制位將產(chǎn)生直接模式錯誤中斷。
三、DMA初始化結(jié)構(gòu)體
typedef struct {uint32_t DMA_Channel; //通道選擇uint32_t DMA_PeripheralBaseAddr; //外設(shè)地址uint32_t DMA_Memory0BaseAddr; //存儲器 0 地址uint32_t DMA_DIR; //傳輸方向uint32_t DMA_BufferSize; //數(shù)據(jù)數(shù)目uint32_t DMA_PeripheralInc; //外設(shè)遞增uint32_t DMA_MemoryInc; //存儲器遞增uint32_t DMA_PeripheralDataSize; //外設(shè)數(shù)據(jù)寬度uint32_t DMA_MemoryDataSize; //存儲器數(shù)據(jù)寬度uint32_t DMA_Mode; //模式選擇uint32_t DMA_Priority; //優(yōu)先級uint32_t DMA_FIFOMode; //FIFO 模式uint32_t DMA_FIFOThreshold; //FIFO 閾值uint32_t DMA_MemoryBurst; //存儲器突發(fā)傳輸uint32_t DMA_PeripheralBurst; //外設(shè)突發(fā)傳輸 } DMA_InitTypeDef;-
DMA_Channel: DMA 請求通道選擇,可選通道 0 至通道 7,每個外 設(shè)對應(yīng)固定的通道,DMA_SxCR :CHSEL[2:0]。
-
DMA_PeripheralBaseAddr: 外設(shè)地址, DMA_SxPAR。
-
DMA_Memory0BaseAddr:存儲器 0 地址,DMA_SxM0AR。
-
DMA_DIR:傳輸方向選擇,可選外設(shè)到存儲器、存儲器到外設(shè)以及存儲 器到存儲器,DMA_SxCR :DIR[1:0]。
-
DMA_BufferSize:設(shè)定一次傳輸?shù)臄?shù)據(jù)個數(shù),DMA_SxNDTR 。
-
DMA_PeripheralInc:外設(shè)地址是否遞增, DMA_SxCR :PINC 。
-
DMA_MemoryInc:存儲器地址是否遞增,DMA_SxCR :MINC。
-
DMA_PeripheralDataSize:外設(shè)數(shù)據(jù)寬度,可選字節(jié)(8 位)、半 字(16 位)和字(32位),DMA_SxCR :PSIZE[1:0]。
-
DMA_BufferSize:設(shè)定一次傳輸?shù)臄?shù)據(jù)個數(shù),DMA_SxNDTR 。
-
DMA_MemoryDataSize:存儲器數(shù)據(jù)寬度,可選字節(jié)(8 位)、半字 (16 位)和字(32位),DMA_SxCR :MSIZE[1:0]。
-
DMA_Mode :DMA傳輸模式選擇,可選一次傳輸或者循環(huán)傳輸 ,DMA_SxCR :CIRC 位的值。
-
DMA_Priority:優(yōu)先級,非常高、高、中和低,DMA_SxCR :PL[1:0]
-
DMA_FIFOMode: FIFO 模式使能,DMA_SxFCR :DMDIS 。
-
DMA_FIFOThreshold: FIFO 閾值選擇,1/4、1/2、 3/4 和滿, DMA_SxFCR :FTH[1:0]。
-
DMA_MemoryBurst:存儲器突發(fā)模式選擇,單次模式、 4 節(jié)拍、 8 節(jié)拍、16 節(jié)拍,DMA_SxCR :MBURST[1:0] 。
-
DMA_PeripheralBurst:外設(shè)突發(fā)模式選擇,單次模式、 4 節(jié)拍、 8 節(jié)拍、16 節(jié)拍,DMA_SxCR :PBURST[1:0] 。
四、編程時需要用到的固件庫函數(shù)
1-初始化DMA的寄存器到復(fù)位狀態(tài)
DMA_DeInit(DMA_Stream_TypeDef* DMAy_Streamx);2-DMA初始化函數(shù)
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStr3-DMA使能函數(shù)
DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState);五、實踐
(1)存儲器到存儲器:FLASH to SRAM,把內(nèi)部Flash的數(shù)據(jù)傳輸?shù)絻?nèi)部的SRAM。
在Flash中定義好要傳輸?shù)臄?shù)據(jù),在SRAM中定義好用來接收Flash數(shù)據(jù)的變量。
確定使用DMA2,哪個數(shù)據(jù)流,哪個通道,然后定義為宏,方便修改。
初始化DMA,主要是配置DMA初始化結(jié)構(gòu)體。
如何配置要參考《STM32F429手冊》流的配置過程:
?
4.編寫數(shù)據(jù)比較函數(shù)
5.編寫main函數(shù)
初始化相關(guān)宏定義:
/* 相關(guān)宏定義,使用存儲器到存儲器傳輸必須使用DMA2 */ #define DMA_STREAM DMA2_Stream0 #define DMA_CHANNEL DMA_Channel_0 #define DMA_STREAM_CLOCK RCC_AHB1Periph_DMA2 #define DMA_FLAG_TCIF DMA_FLAG_TCIF0#define BUFFER_SIZE 32 #define TIMEOUT_MAX 10000 /* Maximum timeout value */定義想要傳輸?shù)臄?shù)據(jù)源以及目標(biāo)存儲器:
/* 定義aSRC_Const_Buffer數(shù)組作為DMA傳輸數(shù)據(jù)源const關(guān)鍵字將aSRC_Const_Buffer數(shù)組變量定義為常量類型 */ const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80}; /* 定義DMA傳輸目標(biāo)存儲器 */ uint32_t aDST_Buffer[BUFFER_SIZE];DMA傳輸配置:
static void DMA_Config(void) {DMA_InitTypeDef DMA_InitStructure;__IO uint32_t Timeout = TIMEOUT_MAX;/* 使能DMA時鐘 */RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);/* 復(fù)位初始化DMA數(shù)據(jù)流 */DMA_DeInit(DMA_STREAM);/* 確保DMA數(shù)據(jù)流復(fù)位完成 */while (DMA_GetCmdStatus(DMA_STREAM) != ENABLE){}/* DMA數(shù)據(jù)流通道選擇 */DMA_InitStructure.DMA_Channel = DMA_CHANNEL; /* 源數(shù)據(jù)地址 */DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;/* 目標(biāo)地址 */DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;/* 存儲器到存儲器模式 */DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;/* 數(shù)據(jù)數(shù)目 */DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE;/* 使能自動遞增功能 */DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;/* 使能自動遞增功能 */DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;/* 源數(shù)據(jù)是字大小(32位) */DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;/* 目標(biāo)數(shù)據(jù)也是字大小(32位) */DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;/* 一次傳輸模式,存儲器到存儲器模式不能使用循環(huán)傳輸 */DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;/* DMA數(shù)據(jù)流優(yōu)先級為高 */DMA_InitStructure.DMA_Priority = DMA_Priority_High;/* 禁用FIFO模式 */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數(shù)據(jù)流參數(shù)配置 */DMA_Init(DMA_STREAM, &DMA_InitStructure);/* 清除DMA數(shù)據(jù)流傳輸完成標(biāo)志位 */DMA_ClearFlag(DMA_STREAM,DMA_FLAG_TCIF);/* 使能DMA數(shù)據(jù)流,開始DMA數(shù)據(jù)傳輸 */DMA_Cmd(DMA_STREAM, ENABLE);/* 檢測DMA數(shù)據(jù)流是否有效并帶有超時檢測功能 */Timeout = TIMEOUT_MAX;while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0)){}/* 判斷是否超時 */if (Timeout == 0){/* 超時就讓程序運(yùn)行下面循環(huán):RGB彩色燈閃爍 */while (1){ LED_RED;Delay(0xFFFFFF);LED_RGBOFF;Delay(0xFFFFFF);}} }要注意的是,上面使用的是單次模式,第一次配置的時候以為是直接模式,就很納悶,存儲器到存儲器不應(yīng)該不可以是直接模式嗎,其實使用存儲器到存儲器模式時,不允許使用循環(huán)模式和直接模式,只能使用單次傳輸和FIFO緩沖。而且只有DMA2控制器能夠執(zhí)行存儲器到存儲器的傳輸。
之后再加入一個傳輸完成的檢測函數(shù):
uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength) {/* 數(shù)據(jù)長度遞減 */while(BufferLength--){/* 判斷兩個數(shù)據(jù)源是否對應(yīng)相等 */if(*pBuffer != *pBuffer1){/* 對應(yīng)數(shù)據(jù)源不相等馬上退出函數(shù),并返回0 */return 0;}/* 遞增兩個數(shù)據(jù)源的地址指針 */pBuffer++;pBuffer1++;}/* 完成判斷并且對應(yīng)數(shù)據(jù)相對 */return 1; }之后我們通過led的亮燈情況來判斷是否傳輸成功:
int main(void) {/* 定義存放比較結(jié)果變量 */uint8_t TransferStatus;/* LED 端口初始化 */LED_GPIO_Config();/* 設(shè)置RGB彩色燈為紫色 */LED_PURPLE; /* 簡單延時函數(shù) */Delay(0xFFFFFF); /* DMA傳輸配置 */DMA_Config(); /* 等待DMA傳輸完成 */while(DMA_GetFlagStatus(DMA_STREAM,DMA_FLAG_TCIF)==DISABLE){} /* 比較源數(shù)據(jù)與傳輸后數(shù)據(jù) */TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);/* 判斷源數(shù)據(jù)與傳輸后數(shù)據(jù)比較結(jié)果*/if(TransferStatus==0) {/* 源數(shù)據(jù)與傳輸后數(shù)據(jù)不相等時RGB彩色燈顯示紅色 */LED_RED;}else{ /* 源數(shù)據(jù)與傳輸后數(shù)據(jù)相等時RGB彩色燈顯示藍(lán)色 */LED_BLUE;}while (1){ } }(2)存儲器到外設(shè):SRAM to 串口,同時LED燈閃爍,演示DMA傳數(shù)據(jù)不需要占用CPU。
初始化對應(yīng)串口:
void Debug_USART_Config(void) {GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK|DEBUG_USART_TX_GPIO_CLK,ENABLE);/* 使能 USART 時鐘 */RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE);/* GPIO初始化 */GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/* 配置Tx引腳為復(fù)用功能 */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN ; GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);/* 配置Rx引腳為復(fù)用功能 */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);/* 連接 PXx 到 USARTx_Tx*/GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT,DEBUG_USART_RX_SOURCE,DEBUG_USART_RX_AF);/* 連接 PXx 到 USARTx__Rx*/GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT,DEBUG_USART_TX_SOURCE,DEBUG_USART_TX_AF);/* 配置串DEBUG_USART 模式 *//* 波特率設(shè)置:DEBUG_USART_BAUDRATE */USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;/* 字長(數(shù)據(jù)位+校驗位):8 */USART_InitStructure.USART_WordLength = USART_WordLength_8b;/* 停止位:1個停止位 */USART_InitStructure.USART_StopBits = USART_StopBits_1;/* 校驗位選擇:不使用校驗 */USART_InitStructure.USART_Parity = USART_Parity_No;/* 硬件流控制:不使用硬件流 */USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;/* USART模式控制:同時使能接收和發(fā)送 */USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;/* 完成USART初始化配置 */USART_Init(DEBUG_USART, &USART_InitStructure);/* 使能串口 */USART_Cmd(DEBUG_USART, ENABLE); }初始化DMA:
//DMA #define DEBUG_USART_DR_BASE (USART1_BASE+0x04) #define SENDBUFF_SIZE 5000 //發(fā)送的數(shù)據(jù)量#define DEBUG_USART_DMA_CLK RCC_AHB1Periph_DMA2 #define DEBUG_USART_DMA_CHANNEL DMA_Channel_4 #define DEBUG_USART_DMA_STREAM DMA2_Stream7void MtoP_DMA_Config(void) {DMA_InitTypeDef DMA_InitStructure;/*開啟DMA時鐘*/RCC_AHB1PeriphClockCmd(DEBUG_USART_DMA_CLK, ENABLE);/* 復(fù)位初始化DMA數(shù)據(jù)流 */DMA_DeInit(DEBUG_USART_DMA_STREAM);/* 確保DMA數(shù)據(jù)流復(fù)位完成 */while (DMA_GetCmdStatus(DEBUG_USART_DMA_STREAM) != DISABLE) {}/*usart1 tx對應(yīng)dma2,通道4,數(shù)據(jù)流7*/ DMA_InitStructure.DMA_Channel = DEBUG_USART_DMA_CHANNEL; /*設(shè)置DMA源:串口數(shù)據(jù)寄存器地址*/DMA_InitStructure.DMA_PeripheralBaseAddr = DEBUG_USART_DR_BASE; /*內(nèi)存地址(要傳輸?shù)淖兞康闹羔?*/DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff;/*方向:從內(nèi)存到外設(shè)*/ DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; /*傳輸大小DMA_BufferSize=SENDBUFF_SIZE*/ DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;/*外設(shè)地址不增*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*內(nèi)存地址自增*/DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /*外設(shè)數(shù)據(jù)單位*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;/*內(nèi)存數(shù)據(jù)單位 8bit*/DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; /*DMA模式:不斷循環(huán)*///DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; /*優(yōu)先級:中*/ DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; /*禁用FIFO*/DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; /*存儲器突發(fā)傳輸 單次傳輸*/DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; /*外設(shè)突發(fā)傳輸 單次傳輸*/DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;/*配置DMA2的數(shù)據(jù)流7*/ DMA_Init(DEBUG_USART_DMA_STREAM, &DMA_InitStructure);/*使能DMA*/DMA_Cmd(DEBUG_USART_DMA_STREAM, ENABLE);/* 等待DMA數(shù)據(jù)流有效*/while(DMA_GetCmdStatus(DEBUG_USART_DMA_STREAM) != ENABLE){} }之后,我們在main函數(shù)中實現(xiàn)邊點(diǎn)燈邊從dma發(fā)送數(shù)據(jù)的功能
int main(void) { uint16_t i;LED_GPIO_Config();/*初始化USART 配置模式為 115200 8-N-1,中斷接收*/Debug_USART_Config();/*填充將要發(fā)送的數(shù)據(jù)*/for(i=0;i<SENDBUFF_SIZE;i++){SendBuff[i] = 'A';}MtoP_DMA_Config();USART_DMACmd(DEBUG_USART, USART_DMAReq_Tx, ENABLE);while(1){ LED1_TOGGLEDelay(0xFFFFF);} }DMA就總結(jié)到這啦,明天就國慶了,那就國慶節(jié)快樂!!在假期多做一些有意義的事情8!!
總結(jié)
以上是生活随笔為你收集整理的STM32F429入门(十八):DMA的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 视+AR为央视网络春晚助阵,打造央视影音
- 下一篇: Jmeter 性能测试入门 ——性能插件