DMA叫做直接存儲器訪問,用于在外設(shè)與存儲器之間與存儲器之間提供高速數(shù)據(jù)傳輸。可以在無需任何CPU操作的情況下通過DMA快速移動數(shù)據(jù)。這樣節(jié)省的CPU資源可供其他操作使用。從硬件層次上來說,DMA 控制器是獨(dú)立于 Cortex-M4內(nèi)核的,有點(diǎn)類似 GPIO、USART 外設(shè)一般,只是 DMA的功能是可以快速移動內(nèi)存數(shù)據(jù)。
要實(shí)現(xiàn)把外設(shè)數(shù)據(jù)寫入內(nèi)存,或把內(nèi)存數(shù)據(jù)送至外設(shè),一般由CPU控制完成,可采用標(biāo)記查詢或中斷的方式。利用中斷傳送數(shù)據(jù),可大大提高CPU的利用率。但當(dāng)批量傳輸數(shù)據(jù)時,由于進(jìn)出中斷需保護(hù)現(xiàn)場和斷點(diǎn),無形之中增加了CPU的中斷負(fù)載。另外,CPU的核心工作是算法處理,借用CPU進(jìn)行大量數(shù)據(jù)傳輸,會降低CPU處理算法的效率。引入DMA技術(shù),在一定程度上解放了CPU,提高了CPU的處理能力。
STM32F4xx的兩個 DMA 控制器總共有 16 個數(shù)據(jù)流(每個控制器 8 個),每一個 DMA控制器都用于管理一個或多個外設(shè)的存儲器訪問請求。每個數(shù)據(jù)流總共可以有多達(dá) 8 個通道(或稱請求)。每個通道都有一個仲裁器,用于處理 DMA 請求間的優(yōu)先級。
DMA控制器結(jié)構(gòu)框圖
DMA 控制器執(zhí)行直接存儲器傳輸:因?yàn)椴捎?AHB 主總線,它可以控制 AHB 總線矩陣來啟動 AHB 事務(wù)。
它可以執(zhí)行下列事務(wù):
● 外設(shè)到存儲器的傳輸
● 存儲器到外設(shè)的傳輸
● 存儲器到存儲器的傳輸(僅DMA2數(shù)據(jù)流能實(shí)現(xiàn)存儲器到存儲器的傳輸)
通道選擇
每個數(shù)據(jù)流可連接8個通道,某一時刻具體使用數(shù)據(jù)流的哪個通道,需通過DMA_SxCR寄存器中的CHSEL[2:0]位控制。通道選擇如下圖所示:
兩個DMA控制器的通道(請求)與數(shù)據(jù)流的映射關(guān)系如下所示:
a.DMA1通道選擇(請求映射)
b.DMA2通道選擇(請求映射)
由表可知,STM32F4XX的每個DMA控制器有8個數(shù)據(jù)流,每個數(shù)據(jù)流有多達(dá)8個通道。
仲裁器
當(dāng)有多個數(shù)據(jù)流同時請求數(shù)據(jù)傳輸時,由仲裁器裁決數(shù)據(jù)流的傳輸順序。
仲裁器為兩個 AHB 主端口(存儲器和外設(shè)端口)提供基于請求優(yōu)先級的 8 個 DMA 數(shù)據(jù)流請求管理,并啟動外設(shè)/存儲器訪問序列。
優(yōu)先級管理分為兩個階段:
● ** 軟件** :每個數(shù)據(jù)流優(yōu)先級都可以在 DMA_SxCR 寄存器中配置。分為四個級別:
— 非常高優(yōu)先級
— 高優(yōu)先級
— 中優(yōu)先級
— 低優(yōu)先級
● 硬件 :如果兩個請求具有相同的軟件優(yōu)先級,則編號低的數(shù)據(jù)流優(yōu)先于編號高的數(shù)據(jù)流。例如,數(shù)據(jù)流 2 的優(yōu)先級高于數(shù)據(jù)流 4。
配置好優(yōu)先級,還需要指定數(shù)據(jù)傳輸?shù)姆较颉⒅羔樳f增方式和循環(huán)模式。
DMA的核心設(shè)置集中在配置寄存器、指定內(nèi)存地址、外設(shè)地址及數(shù)據(jù)項(xiàng)目。下面描述DMA的相關(guān)寄存器。
DMA 低中斷狀態(tài)寄存器 (DMA_LISR)
DMA 高中斷狀態(tài)寄存器 (DMA_HISR)
DMA控制器使用狀態(tài)寄存器LISR和HISR,來描述8個數(shù)據(jù)流的狀態(tài)標(biāo)記。LISR描述低4個數(shù)據(jù)流,而HISR描述高4個數(shù)據(jù)流。每個流對應(yīng)5個狀態(tài),下面以數(shù)據(jù)流x來進(jìn)行說明。
FCIFx :數(shù)據(jù)流x傳輸完成中斷標(biāo)記;
HTIFx :數(shù)據(jù)流x半傳輸完成中斷標(biāo)記;
TEIFx :數(shù)據(jù)流x傳輸錯誤中斷標(biāo)記;
DMEIFx:數(shù)據(jù)流x直接傳輸錯誤中斷標(biāo)記;
FEIFx :數(shù)據(jù)流xFIFO錯誤中斷標(biāo)記。
DMA 低中斷標(biāo)志清零寄存器 (DMA_LIFCR)
DMA 高中斷標(biāo)志清零寄存器 (DMA_HIFCR)
由DMA_LIFR/DMA_HIFR寄存器可知,DMA的標(biāo)記是只讀的,不能直接擦除。DMA控制器含有中斷標(biāo)記清零寄存器,用戶通過向中斷標(biāo)記清零寄存器對應(yīng)位寫1,可清除對應(yīng)的中斷標(biāo)記。
DMA 數(shù)據(jù)流 x 配置寄存器 (DMA_SxCR) (x = 0..7)
位 27:25 CHSEL[2:0]:通道選擇 (Channel selection)
這些位將由軟件置 1 和清零。
000:選擇通道 0
001:選擇通道 1
010:選擇通道 2
011:選擇通道 3
100:選擇通道 4
101:選擇通道 5
110:選擇通道 6
111:選擇通道 7
這些位受到保護(hù),只有 EN 為“0”時才可以寫入
位 24:23 MBURST :存儲器突發(fā)傳輸配置(Memory burst transfer configuration)
這些位將由軟件置 1 和清零。
00:單次傳輸
01:INCR4(4 個節(jié)拍的增量突發(fā)傳輸)
10:INCR8(8 個節(jié)拍的增量突發(fā)傳輸)
11:INCR16(16 個節(jié)拍的增量突發(fā)傳輸)
這些位受到保護(hù),只有 EN 為“0”時才可以寫入
在直接模式中,當(dāng)位 EN =“1”時,這些位由硬件強(qiáng)制置為 0x0。
位 22:21 PBURST[1:0]:外設(shè)突發(fā)傳輸配置 (Peripheral burst transfer configuration)
這些位將由軟件置 1 和清零。
00:單次傳輸
01:INCR4(4 個節(jié)拍的增量突發(fā)傳輸)
10:INCR8(8 個節(jié)拍的增量突發(fā)傳輸)
11:INCR16(16 個節(jié)拍的增量突發(fā)傳輸)
這些位受到保護(hù),只有 EN 為“0”時才可以寫入
在直接模式下,這些位由硬件強(qiáng)制置為 0x0。
位 19 CT:當(dāng)前目標(biāo)(僅在雙緩沖區(qū)模式下)(Current target (only in double buffer mode))
此位由硬件置 1 和清零,也可由軟件寫入。
0:當(dāng)前目標(biāo)存儲器為存儲器 0(使用 DMA_SxM0AR 指針尋址)
1:當(dāng)前目標(biāo)存儲器為存儲器 1(使用 DMA_SxM1AR 指針尋址)
只有 EN 為“0”時,此位才可以寫入,以指示第一次傳輸?shù)哪繕?biāo)存儲區(qū)。在使能數(shù)據(jù)流后,此位相當(dāng)于一個狀態(tài)標(biāo)志,用于指示作為當(dāng)前目標(biāo)的存儲區(qū)。
位 18 DBM:雙緩沖區(qū)模式 (Double buffer mode)
此位由軟件置 1 和清零。
0:傳輸結(jié)束時不切換緩沖區(qū)
1:DMA 傳輸結(jié)束時切換目標(biāo)存儲區(qū)
此位受到保護(hù),只有 EN 為“0”時才可以寫入。
位 17:16 PL[1:0]:優(yōu)先級 (Priority level)
這些位將由軟件置 1 和清零。
00:低
01:中
10:高
11:非常高
這些位受到保護(hù),只有 EN 為“0”時才可以寫入。
位 15 PINCOS:外設(shè)增量偏移量 (Peripheral increment offset size)
此位由軟件置 1 和清零
0:用于計算外設(shè)地址的偏移量與 PSIZE 相關(guān)
1:用于計算外設(shè)地址的偏移量固定為 4(32 位對齊)。
如果位 PINC =“0”,則此位沒有意義。
此位受到保護(hù),只有 EN 為“0”時才可以寫入。
如果選擇直接模式或者 PBURST 不等于“00”,則當(dāng)使能數(shù)據(jù)流(位 EN =“1”)時,此位由硬件強(qiáng)制置為低電平。
位 14:13 MSIZE[1:0]:存儲器數(shù)據(jù)大小 (Memory data size)
這些位將由軟件置 1 和清零。
00:字節(jié)(8 位)
01:半字(16 位)
10:字(32 位)
11:保留
這些位受到保護(hù),只有 EN 為“0”時才可以寫入。
在直接模式下,當(dāng)位 EN =“1”時,MSIZE 位由硬件強(qiáng)制置為與 PSIZE 相同的值。
位 12:11 PSIZE[1:0]:外設(shè)數(shù)據(jù)大小 (Peripheral data size)
這些位將由軟件置 1 和清零。
00:字節(jié)(8 位)
01:半字(16 位)
10:字(32 位)
11:保留
這些位受到保護(hù),只有 EN 為“0”時才可以寫入
位 10 MINC:存儲器遞增模式 (Memory increment mode)
此位由軟件置 1 和清零。
0:存儲器地址指針固定
1:每次數(shù)據(jù)傳輸后,存儲器地址指針遞增(增量為 MSIZE 值)
此位受到保護(hù),只有 EN 為“0”時才可以寫入。
位 9 PINC:外設(shè)遞增模式 (Peripheral increment mode)
此位由軟件置 1 和清零。
0:外設(shè)地址指針固定
1:每次數(shù)據(jù)傳輸后,外設(shè)地址指針遞增(增量為 PSIZE 值)
此位受到保護(hù),只有 EN 為“0”時才可以寫入。
位 8 CIRC:循環(huán)模式 (Circular mode)
此位由軟件置 1 和清零,并可由硬件清零。
0:禁止循環(huán)模式
1:使能循環(huán)模式
如果外設(shè)為流控制器(位 PFCTRL=1)且使能數(shù)據(jù)流(位 EN=1),此位由硬件自動強(qiáng)制清零。
如果 DBM 位置 1,當(dāng)使能數(shù)據(jù)流(位 EN =“1”)時,此位由硬件自動強(qiáng)制置 1。
位 7:6 DIR[1:0]:數(shù)據(jù)傳輸方向 (Data transfer direction)
這些位將由軟件置 1 和清零。
00:外設(shè)到存儲器
01:存儲器到外設(shè)
10:存儲器到存儲器
11:保留
這些位受到保護(hù),只有 EN 為“0”時才可以寫入。
位 5 PFCTRL:外設(shè)流控制器 (Peripheral flow controller)
此位由軟件置 1 和清零。
0:DMA 是流控制器
1:外設(shè)是流控制器
此位受到保護(hù),只有 EN 為“0”時才可以寫入。
選擇存儲器到存儲器模式(位 DIR[1:0]=10)后,此位由硬件自動強(qiáng)制清零。
位 4 TCIE:傳輸完成中斷使能 (Transfer complete interrupt enable)
此位由軟件置 1 和清零。
0:禁止 TC 中斷
1:使能 TC 中斷
位 3 HTIE:半傳輸中斷使能 (Half transfer interrupt enable)
此位由軟件置 1 和清零。
0:禁止 HT 中斷
1:使能 HT 中斷
位 2 TEIE:傳輸錯誤中斷使能 (Transfer error interrupt enable)
此位由軟件置 1 和清零。
0:禁止 TE 中斷
1:使能 TE 中斷
位 1 DMEIE:直接模式錯誤中斷使能 (Direct mode error interrupt enable)
此位由軟件置 1 和清零。
0:禁止 DME 中斷
1:使能 DME 中斷
位 0 EN:數(shù)據(jù)流使能/讀作低電平時數(shù)據(jù)流就緒標(biāo)志 (Stream enable / flag stream ready when read low)
此位由軟件置 1 和清零。
0:禁止數(shù)據(jù)流
1:使能數(shù)據(jù)流
以下情況下,此位可由硬件清零:
— DMA 傳輸結(jié)束時(準(zhǔn)備好配置數(shù)據(jù)流)
— AHB 主總線出現(xiàn)傳輸錯誤時
— 存儲器 AHB 端口上的 FIFO 閾值與突發(fā)大小不兼容時
此位讀作 0 時,軟件可以對配置和 FIFO 位寄存器編程。EN 位讀作 1 時,禁止向這些寄存器執(zhí)行寫操作。
另外,在EN位置1(啟動流前),與數(shù)據(jù)流相對應(yīng)的事件標(biāo)記都要清0。
DMA 數(shù)據(jù)流 x 數(shù)據(jù)項(xiàng)數(shù)寄存器 (DMA_SxNDTR) (x = 0..7)
位 15:0 NDT[15:0]:要傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目 (Number of data items to transfer)
要傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)目(0 到 65535)。只有在禁止數(shù)據(jù)流時,才能向此寄存器執(zhí)行寫操作。
使能數(shù)據(jù)流后,此寄存器為只讀,用于指示要傳輸?shù)氖S鄶?shù)據(jù)項(xiàng)數(shù)。每次 DMA 傳輸后,此寄存器將遞減。
傳輸完成后,此寄存器保持為零(數(shù)據(jù)流處于正常模式時),或者在以下情況下自動以先前編程的值重載:
— 以循環(huán)模式配置數(shù)據(jù)流時。
— 通過將 EN 位置“1”來重新使能數(shù)據(jù)流時
如果該寄存器的值為零,則即使使能數(shù)據(jù)流,也無法完成任何事務(wù)。
DMA 數(shù)據(jù)流 x 外設(shè)地址寄存器 (DMA_SxPAR) (x = 0..7)
位 31:0 PAR[31:0]:外設(shè)地址 (Peripheral address)
讀/寫數(shù)據(jù)的外設(shè)數(shù)據(jù)寄存器的基址。
這些位受到寫保護(hù),只有 DMA_SxCR 寄存器中的 EN 為“0”時才可以寫入。
DMA相當(dāng)于CPU的助理,通過軟件對DMA控制器的以上寄存器進(jìn)行合理配置,就可以啟動DMA來傳輸數(shù)據(jù)了。此時CPU就可以專注于運(yùn)算或其它事務(wù)的處理了。
這里以串口DMA為例編寫程序
void DMA2_Uart1_TX_Init()
{
//1. 開外設(shè)時鐘(DMA2)
RCC- >AHB1ENR |= 1< 22;
//2.配置DMA控制器
//a. 先禁止DMA,然后再設(shè)置
//清狀態(tài)標(biāo)記(寫1清除)
DMA2- >LIFCR = 0X0F7D0F7D;
DMA2- >HIFCR = 0X0F7D0F7D;
//禁止EN
DMA2_Stream7- >CR = 0;
while((DMA2_Stream7- >CR & (1< 0)) != 0); //等待DMA停止
//b. 配置DMA/采用單次模式
DMA2_Stream7- >CR |= 4< 25; //通道4
DMA2_Stream7- >CR |= 2< 16; //優(yōu)先級:2
//c. PINCOS不偏移(不固定4字節(jié)對齊)、內(nèi)存/外設(shè)大小為一個字節(jié)
//內(nèi)存遞增、外設(shè)不遞增
DMA2_Stream7- >CR |= 1< 10;
//不采用循環(huán)模式、方向:內(nèi)存到外設(shè)
DMA2_Stream7- >CR |= 1< 6;
}
/*
傳輸設(shè)置函數(shù)
paddr:外設(shè)地址
maddr:內(nèi)存地址
cnt:數(shù)據(jù)項(xiàng)數(shù)目
*/
void DMA2_TransConfig(u32 paddr,u32 maddr,u16 cnt)
{
//清狀態(tài)標(biāo)記(寫1清除)
DMA2- >LIFCR = 0X0F7D0F7D;
DMA2- >HIFCR = 0X0F7D0F7D;
DMA2_Stream7- >CR &=~(1< 0);
while((DMA2_Stream7- >CR & (1< 0)) != 0); //等待DMA停止
DMA2_Stream7- >PAR = paddr;
DMA2_Stream7- >M0AR = maddr;
DMA2_Stream7- >NDTR = cnt;
DMA2_Stream7- >CR |= 1< 0; //啟動傳輸
}
u8 DMA2_GetFlag() //獲取傳輸完成標(biāo)記
{
if(DMA2- >HISR &(1< 27)) //判斷是否傳輸完成
return 1;
else
return 0;
}
u8 DMA2_ClearFlag()
{
DMA2- >HIFCR = 1< 27; //清傳輸完成標(biāo)記
}
除了以上DMA控制器的配置外,還要開啟串口的DMA功能。
void Usart1_dma_config()
{
USART1- >CR3 |= 1< 7; //使能串口DMA發(fā)送功能
}
接著編寫主函數(shù)進(jìn)行測試:
#include "stm32f4xx.h"
#include "usart.h"
#include "DMA.h"
#include "delay.h"
u8 str[]="hello,world!rn";
int main()
{
u32 i=0;
Usart1_Init(460800);
Usart1_dma_config(); //開啟串口發(fā)送DMA功能
DMA2_Uart1_TX_Init(); //DMA控制器初始化
DMA2_TransConfig((u32)&USART1- >DR,(u32)str,sizeof(str)); //開始傳輸
while(1)
{
while(DMA2_GetFlag() == 0); //判斷傳輸是否完成,完成時退出循環(huán)
DMA2_ClearFlag(); //清傳輸完成標(biāo)記
DMA2_TransConfig((u32)&USART1- >DR,(u32)str,sizeof(str)); //再次開啟傳輸
Delay_ms(500);
}
}
由于DMA的傳輸速度較快,所以把串口的波特率調(diào)到最高460800,但是速度還是太快,串口接收的速度跟不上,如下圖所示:
因此在大循環(huán)的最后加上500ms的延時,就可以看到傳輸?shù)臄?shù)據(jù)被完整地打印出來了。可見DMA傳輸速度之快。
串口DMA發(fā)送數(shù)據(jù)測試成功。串口DMA發(fā)送數(shù)據(jù)是屬于內(nèi)存到外設(shè)的,對于外設(shè)到內(nèi)存和內(nèi)存到內(nèi)存的應(yīng)用同樣只要根據(jù)以上寄存器的描述進(jìn)行配置,就可以實(shí)現(xiàn)DMA的數(shù)據(jù)傳輸了。
評論
查看更多