找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 3648|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

STM32 DMA 詳解

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:86860 發(fā)表于 2015-7-26 01:48 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
DMA,全稱(chēng)為:Direct Memory Access,即直接存儲(chǔ)器訪問(wèn),DMA  傳輸將數(shù)據(jù)從一個(gè)地址空間復(fù)制到另外一個(gè)地址空間。當(dāng)  CPU  初始化這個(gè)傳輸動(dòng)作,傳輸動(dòng)作本身是由DMA  控制器  來(lái)實(shí)行和完成。典型的例子就是移動(dòng)一個(gè)外部?jī)?nèi)存的區(qū)塊到芯片內(nèi)部更快的內(nèi)存區(qū)。像是這樣的操作并沒(méi)有讓處理器工作拖延,反而可以被重新排程去處理其他的工

作。DMA  傳輸對(duì)于高效能嵌入式系統(tǒng)算法和網(wǎng)絡(luò)是很重要的。DMA 傳輸方式無(wú)需 CPU 直接控制傳輸,也沒(méi)有中斷處理方式那樣保留現(xiàn)場(chǎng)和恢復(fù)現(xiàn)場(chǎng)的過(guò)程,通過(guò)硬件為 RAM 與 I/O 設(shè)備開(kāi)辟一條直接傳送數(shù)據(jù)的通路,能使 CPU 的效率大為提高。DMA 是個(gè)非常好的功能,它不但能減輕 CPU 負(fù)擔(dān),還能提高數(shù)據(jù)傳輸速度

STM32 最多有 個(gè) DMA 控制器(DMA2 僅存在大容量產(chǎn)品中),DMA1 有 個(gè)通道。DMA2 有 5個(gè)通道。每個(gè)通道專(zhuān)門(mén)用來(lái)管理來(lái)自于一個(gè)或多個(gè)外設(shè)對(duì)存儲(chǔ)器訪問(wèn)的請(qǐng)求。還有一個(gè)仲裁起來(lái)協(xié)調(diào)各個(gè) DMA 請(qǐng)求的優(yōu)先權(quán)。

STM32 的 DMA 有以下一些特性: 

●每個(gè)通道都直接連接專(zhuān)用的硬件 DMA 請(qǐng)求,每個(gè)通道都同樣支持軟件觸發(fā)。這些功能通過(guò)軟件來(lái)配置。 

●在七個(gè)請(qǐng)求間的優(yōu)先權(quán)可以通過(guò)軟件編程設(shè)置(共有四級(jí):很高、高、中等和低),假如在相等優(yōu)先權(quán)時(shí)由硬件決定(請(qǐng)求 優(yōu)先于請(qǐng)求 1,依此類(lèi)推)  。 

●獨(dú)立的源和目標(biāo)數(shù)據(jù)區(qū)的傳輸寬度(字節(jié)、半字、全字),模擬打包和拆包的過(guò)程。源和目標(biāo)地址必須按數(shù)據(jù)傳輸寬度對(duì)齊。 

●支持循環(huán)的緩沖器管理 

●每個(gè)通道都有 個(gè)事件標(biāo)志(DMA  半傳輸,DMA 傳輸完成和 DMA 傳輸出錯(cuò)),這 個(gè)事件標(biāo)志邏輯或成為一個(gè)單獨(dú)的中斷請(qǐng)求。 

●存儲(chǔ)器和存儲(chǔ)器間的傳輸 

●外設(shè)和存儲(chǔ)器,存儲(chǔ)器和外設(shè)的傳輸 

●閃存、SRAM、外設(shè)的 SRAMAPB1 APB2 和 AHB 外設(shè)均可作為訪問(wèn)的源和目標(biāo)。 

●可編程的數(shù)據(jù)傳輸數(shù)目:最大為 65536

庫(kù)函數(shù)下 DMA1 通道 的配置步驟

1使能 DMA 時(shí)鐘

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  //使能 DMA 時(shí)鐘

2初始化 DMA 通道 參數(shù) 

DMA 通道配置參數(shù)種類(lèi)比較繁多,包括內(nèi)存地址,外設(shè)地址,傳輸數(shù)據(jù)長(zhǎng)度,數(shù)據(jù)寬度,通道優(yōu)先級(jí)等等。這些參數(shù)的配置在庫(kù)函數(shù)中都是在函數(shù) DMA_Init 中完成,下面我們看看函數(shù)定義:

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)

函數(shù)的第一個(gè)參數(shù)是指定初始化的 DMA 通道號(hào),這個(gè)很容易理解,下面我們主要看看第二個(gè)參數(shù)。跟其他外設(shè)一樣,同樣是通過(guò)初始化結(jié)構(gòu)體成員變量值來(lái)達(dá)到初始化的目的,下面我們來(lái)看看 DMA_InitTypeDef 結(jié)構(gòu)體的定義:

    typedef struct

{

uint32_t DMA_PeripheralBaseAddr;

uint32_t DMA_MemoryBaseAddr; 

uint32_t DMA_DIR; 

uint32_t DMA_BufferSize; 

uint32_t DMA_PeripheralInc; 

uint32_t DMA_MemoryInc; 

uint32_t DMA_PeripheralDataSize;

uint32_t DMA_MemoryDataSize; 

uint32_t DMA_Mode; 

uint32_t DMA_Priority; 

uint32_t DMA_M2M; 

}DMA_InitTypeDef;

第一個(gè)參數(shù) DMA_PeripheralBaseAddr 用來(lái)設(shè)置 DMA 傳輸?shù)耐庠O(shè)基地址,比如要進(jìn)行串口DMA 傳輸,那么外設(shè)基地址為串口接受發(fā)送數(shù)據(jù)存儲(chǔ)器 USART1->DR 的地址,表示方法為&USART1->DR。

第二個(gè)參數(shù) DMA_MemoryBaseAddr 內(nèi)存基地址,也就是我們存放 DMA 傳輸數(shù)據(jù)的內(nèi)存地址。

第三個(gè)參數(shù) DMA_DIR 設(shè)置數(shù)據(jù)傳輸方向,決定是從外設(shè)讀取數(shù)據(jù)到內(nèi)存還送從內(nèi)存讀取數(shù)據(jù)發(fā)送到外設(shè),也就是外設(shè)是源地還是目的地,這里我們?cè)O(shè)置為從內(nèi)存讀取數(shù)據(jù)發(fā)送到串口,所以外設(shè)自然就是目的地了,所以選擇值為 DMA_DIR_PeripheralDST。

第四個(gè)參數(shù) DMA_BufferSize 設(shè)置一次傳輸數(shù)據(jù)量的大小

第五個(gè)參數(shù) DMA_PeripheralInc 設(shè)置傳輸數(shù)據(jù)的時(shí)候外設(shè)地址是不變還是遞增。如果設(shè)置為遞增,那么下一次傳輸?shù)臅r(shí)候地址加 1,這里因?yàn)槲覀兪且恢蓖潭ㄍ庠O(shè)地址&USART1->DR發(fā)送數(shù)據(jù),所以地址不遞增,值為 DMA_PeripheralInc_Disable;

第六個(gè)參 數(shù) DMA_MemoryInc 設(shè)置傳輸數(shù)據(jù)時(shí)候內(nèi)存地址是否遞增。 這個(gè)參數(shù) 和DMA_PeripheralInc 意思接近,只不過(guò)針對(duì)的是內(nèi)存。這里我們的場(chǎng)景是將內(nèi)存中連續(xù)存儲(chǔ)單元的數(shù)據(jù)發(fā)送到串口,毫無(wú)疑問(wèn)內(nèi)存地址是需要遞增的,所以值為 DMA_MemoryInc_Enable。

第七個(gè)參數(shù) DMA_PeripheralDataSize 用來(lái)設(shè)置外設(shè)的數(shù)據(jù)長(zhǎng)度是為字節(jié)傳輸(8bits) ,半字傳輸 (16bits) 還 是 字 傳 輸 (32bits) , 這 里 我 們 是 位 字 節(jié) 傳 輸 , 所 以 值 設(shè) 置 為DMA_PeripheralDataSize_Byte。

第八個(gè)參數(shù) DMA_MemoryDataSize 是用來(lái)設(shè)置內(nèi)存的數(shù)據(jù)長(zhǎng)度,和第七個(gè)參數(shù)意思接近,這里我們同樣設(shè)置為字節(jié)傳輸 DMA_MemoryDataSize_Byte。

第九個(gè)參數(shù) DMA_Mode 用來(lái)設(shè)置 DMA 模式是否循環(huán)采集,也就是說(shuō),比如我們要從內(nèi)存中采集 64 個(gè)字節(jié)發(fā)送到串口,如果設(shè)置為重復(fù)采集,那么它會(huì)在 64 個(gè)字節(jié)采集完成之后繼續(xù)從內(nèi)存的第一個(gè)地址采集,如此循環(huán)。這里我們?cè)O(shè)置為一次連續(xù)采集完成之后不循環(huán)。所以設(shè)置值為 DMA_Mode_Normal。在我們下面的實(shí)驗(yàn)中,如果設(shè)置此參數(shù)為循環(huán)采集,那么你會(huì)看到串口不停的打印數(shù)據(jù),不會(huì)中斷,大家在實(shí)驗(yàn)中可以修改這個(gè)參數(shù)測(cè)試一下。

第十個(gè)參數(shù)是設(shè)置 DMA 通道的優(yōu)先級(jí),有低,中,高,超高三種模式,這里我們?cè)O(shè)置優(yōu)先級(jí)別為中級(jí),所以值為 DMA_Priority_Medium。如果要開(kāi)啟多個(gè)通道,那么這個(gè)值就非常有意義。

第 十 一 個(gè) 參 數(shù) DMA_M2M 設(shè) 置 是 否 是 存 儲(chǔ) 器 到 存 儲(chǔ) 器 模 式 傳 輸 , 這 里 我 們 選 擇DMA_M2M_Disable。

實(shí)例代碼:

DMA_InitTypeDef DMA_InitStructure;

DMA_InitStructure.DMA_PeripheralBaseAddr = &USART1->DR;   //DMA 外設(shè) ADC 基地址

DMA_InitStructure.DMA_MemoryBaseAddr = cmar;   //DMA 內(nèi)存基地址

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;   //從內(nèi)存讀取發(fā)送到外設(shè)

DMA_InitStructure.DMA_BufferSize = 64;   //DMA 通道的 DMA 緩存的大小

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設(shè)地址不變

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   //內(nèi)存地址遞增

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;   //工作在正常緩存模式

DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道  x 擁有中優(yōu)先級(jí) 

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   //非內(nèi)存到內(nèi)存?zhèn)鬏?/span>

DMA_Init(DMA_CHx, &DMA_InitStructure);   //根據(jù)指定的參數(shù)初始化

3使能串口 DMA 發(fā)送

進(jìn)行 DMA 配置之后,我們就要開(kāi)啟串口的 DMA 發(fā)送功能,使用的函數(shù)是:

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);

如果是要使能串口 DMA 接受,那么第二個(gè)參數(shù)修改為 USART_DMAReq_Rx 即可。

4使能 DMA1 通道 4,啟動(dòng)傳輸。

使能串口 DMA 發(fā)送之后,我們接著就要使能 DMA 傳輸通道:

DMA_Cmd(DMA_CHx, ENABLE);

通過(guò)以上 步設(shè)置,我們就可以啟動(dòng)一次 USART1 的 DMA 傳輸了。

5查詢 DMA 傳輸狀態(tài)

在 DMA 傳輸過(guò)程中,我們要查詢 DMA 傳輸通道的狀態(tài),使用的函數(shù)是:

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)

比如我們要查詢 DMA 通道 傳輸是否完成,方法是:

DMA_GetFlagStatus(DMA2_FLAG_TC4);

這里還有一個(gè)比較重要的函數(shù)就是獲取當(dāng)前剩余數(shù)據(jù)量大小的函數(shù)

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)

比如我們要獲取 DMA 通道 還有多少個(gè)數(shù)據(jù)沒(méi)有傳輸,方法是:

DMA_GetCurrDataCounter(DMA1_Channel4);




DMA_InitTypeDef DMA_InitStructure;

u16 DMA1_MEM_LEN;//保存DMA每次數(shù)據(jù)傳送的長(zhǎng)度
//DMA1的各通道配置
//這里的傳輸形式是固定的,這點(diǎn)要根據(jù)不同的情況來(lái)修改
//從存儲(chǔ)器->外設(shè)模式/8位數(shù)據(jù)寬度/存儲(chǔ)器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外設(shè)地址
//cmar:存儲(chǔ)器地址
//cndtr:數(shù)據(jù)傳輸量

void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA傳輸
DMA_DeInit(DMA_CHx); //將DMA的通道1寄存器重設(shè)為缺省值
DMA1_MEM_LEN=cndtr;

DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外設(shè)ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA內(nèi)存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //數(shù)據(jù)傳輸方向,從內(nèi)存讀取發(fā)送到外設(shè)
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA緩存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址寄存器不變
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //內(nèi)存地址寄存器遞增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //數(shù)據(jù)寬度為8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //數(shù)據(jù)寬度為8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常緩存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x擁有中優(yōu)先級(jí)
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x沒(méi)有設(shè)置為內(nèi)存到內(nèi)存?zhèn)鬏?br> DMA_Init(DMA_CHx, &DMA_InitStructure); //根據(jù)DMA_InitStruct中指定的參數(shù)初始化DMA的通道USART1_Tx_DMA_Channel所標(biāo)識(shí)的寄存器
}

//開(kāi)啟一次DMA傳輸
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
DMA_Cmd(DMA_CHx, DISABLE ); //關(guān)閉USART1 TX DMA1 所指示的通道
DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//DMA通道的DMA緩存的大小
DMA_Cmd(DMA_CHx, ENABLE); //使能USART1 TX DMA1 所指示的通道
}





u8 SendBuff[5200];
MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,5168);//DMA1通道4,外設(shè)為串口1,存儲(chǔ)器為SendBuff,長(zhǎng)度5168.

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA發(fā)送

MYDMA_Enable(DMA1_Channel4);//開(kāi)始一次DMA傳輸!
if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET) //判斷通道4傳輸完成
{
DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道4傳輸完成標(biāo)志
break;
}
pro=DMA_GetCurrDataCounter(DMA1_Channel4);



分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表