搞嵌入式,不懂DMA?笑死人
DMA,全稱Direct Memory Access,即直接存儲(chǔ)器訪問(wèn)。
本文引用地址:http://cafeforensic.com/article/202404/457231.htmDMA傳輸將數(shù)據(jù)從一個(gè)地址空間復(fù)制到另一個(gè)地址空間,提供在外設(shè)和存儲(chǔ)器之間或者存儲(chǔ)器和存儲(chǔ)器之間的高速數(shù)據(jù)傳輸。
我們知道CPU有轉(zhuǎn)移數(shù)據(jù)、計(jì)算、控制程序轉(zhuǎn)移等很多功能,系統(tǒng)運(yùn)作的核心就是CPU.
CPU無(wú)時(shí)不刻的在處理著大量的事務(wù),但有些事情卻沒(méi)有那么重要,比方說(shuō)數(shù)據(jù)的復(fù)制和存儲(chǔ)數(shù)據(jù),如果我們把這部分的CPU資源拿出來(lái),讓CPU去處理其他的復(fù)雜計(jì)算事務(wù),是不是能夠更好的利用CPU的資源呢?
因此:轉(zhuǎn)移數(shù)據(jù)(尤其是轉(zhuǎn)移大量數(shù)據(jù))是可以不需要CPU參與。比如希望外設(shè)A的數(shù)據(jù)拷貝到外設(shè)B,只要給兩種外設(shè)提供一條數(shù)據(jù)通路,直接讓數(shù)據(jù)由A拷貝到B 不經(jīng)過(guò)CPU的處理,
DMA就是基于以上設(shè)想設(shè)計(jì)的,它的作用就是解決大量數(shù)據(jù)轉(zhuǎn)移過(guò)度消耗CPU資源的問(wèn)題。有了DMA使CPU更專注于更加實(shí)用的操作–計(jì)算、控制等。
DMA定義:
DMA用來(lái)提供在外設(shè)和存儲(chǔ)器之間或者存儲(chǔ)器和存儲(chǔ)器之間的高速數(shù)據(jù)傳輸。無(wú)須CPU的干預(yù),通過(guò)DMA數(shù)據(jù)可以快速地移動(dòng)。這就節(jié)省了CPU的資源來(lái)做其他操作。
DMA傳輸方式
DMA的作用就是實(shí)現(xiàn)數(shù)據(jù)的直接傳輸,而去掉了傳統(tǒng)數(shù)據(jù)傳輸需要CPU寄存器參與的環(huán)節(jié),主要涉及四種情況的數(shù)據(jù)傳輸,但本質(zhì)上是一樣的,都是從內(nèi)存的某一區(qū)域傳輸?shù)絻?nèi)存的另一區(qū)域(外設(shè)的數(shù)據(jù)寄存器本質(zhì)上就是內(nèi)存的一個(gè)存儲(chǔ)單元)。四種情況的數(shù)據(jù)傳輸如下:
外設(shè)到內(nèi)存
內(nèi)存到外設(shè)
內(nèi)存到內(nèi)存
外設(shè)到外設(shè)
DMA傳輸參數(shù)
我們知道,數(shù)據(jù)傳輸,首先需要的是1 數(shù)據(jù)的源地址 2 數(shù)據(jù)傳輸位置的目標(biāo)地址 ,3 傳遞數(shù)據(jù)多少的數(shù)據(jù)傳輸量 ,4 進(jìn)行多少次傳輸?shù)膫鬏斈J?/span> DMA所需要的核心參數(shù),便是這四個(gè)
當(dāng)用戶將參數(shù)設(shè)置好,主要涉及源地址、目標(biāo)地址、傳輸數(shù)據(jù)量這三個(gè),DMA控制器就會(huì)啟動(dòng)數(shù)據(jù)傳輸,當(dāng)剩余傳輸數(shù)據(jù)量為0時(shí) 達(dá)到傳輸終點(diǎn),結(jié)束DMA傳輸 ,當(dāng)然,DMA 還有循環(huán)傳輸模式 當(dāng)?shù)竭_(dá)傳輸終點(diǎn)時(shí)會(huì)重新啟動(dòng)DMA傳輸?! ?也就是說(shuō)只要剩余傳輸數(shù)據(jù)量不是0,而且DMA是啟動(dòng)狀態(tài),那么就會(huì)發(fā)生數(shù)據(jù)傳輸。
DMA的主要特征
每個(gè)通道都直接連接專用的硬件DMA請(qǐng)求,每個(gè)通道都同樣支持軟件觸發(fā)。這些功能通過(guò)軟件來(lái)配置;
在同一個(gè)DMA模塊上,多個(gè)請(qǐng)求間的優(yōu)先權(quán)可以通過(guò)軟件編程設(shè)置(共有四級(jí):很高、高、中等和低),優(yōu)先權(quán)設(shè)置相等時(shí)由硬件決定(請(qǐng)求0優(yōu)先于請(qǐng)求1,依此類推);
獨(dú)立數(shù)據(jù)源和目標(biāo)數(shù)據(jù)區(qū)的傳輸寬度(字節(jié)、半字、全字),模擬打包和拆包的過(guò)程。源和目標(biāo)地址必須按數(shù)據(jù)傳輸寬度對(duì)齊;
支持循環(huán)的緩沖器管理;
每個(gè)通道都有3個(gè)事件標(biāo)志(DMA半傳輸、DMA傳輸完成和DMA傳輸出錯(cuò)),這3個(gè)事件標(biāo)志邏輯或成為一個(gè)單獨(dú)的中斷請(qǐng)求;
存儲(chǔ)器和存儲(chǔ)器間的傳輸、外設(shè)和存儲(chǔ)器、存儲(chǔ)器和外設(shè)之間的傳輸;
閃存、SRAM、外設(shè)的SRAM、APB1、APB2和AHB外設(shè)均可作為訪問(wèn)的源和目標(biāo);
可編程的數(shù)據(jù)傳輸數(shù)目:最大為65535。
STM32少個(gè)DMA資源?
對(duì)于大容量的STM32芯片有2個(gè)DMA控制器 兩個(gè)DMA控制器,DMA1有7個(gè)通道,DMA2有5個(gè)通道。每個(gè)通道都可以配置一些外設(shè)的地址。
①DMA1 controller
從外設(shè)(TIMx[x=1、2、3、4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1、2]和USARTx[x=1、2、3])產(chǎn)生的7個(gè)DMA請(qǐng)求,通過(guò)邏輯或輸入到DMA1控制器 其中每個(gè)通道都對(duì)應(yīng)著具體的外設(shè):
② DMA2 controller
從外設(shè)(TIMx[5、6、7、8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO)產(chǎn)生的5個(gè)請(qǐng)求,經(jīng)邏輯或輸入到DMA2控制器,其中每個(gè)通道都對(duì)應(yīng)著具體的外設(shè):
這些在下方系統(tǒng)框圖中也可以清晰地看到
DMA工作系統(tǒng)框圖
上方的框圖,我們可以看到STM32內(nèi)核,存儲(chǔ)器,外設(shè)及DMA的連接,這些硬件最終通過(guò)各種各樣的線連接到總線矩陣中,硬件結(jié)構(gòu)之間的數(shù)據(jù)轉(zhuǎn)移都經(jīng)過(guò)總線矩陣的協(xié)調(diào),使各個(gè)外設(shè)和諧的使用總線來(lái)傳輸數(shù)據(jù)。我們對(duì)他來(lái)進(jìn)行一點(diǎn)一點(diǎn)的分析:
下面看有與沒(méi)有DMA的情況下,ADC采集的數(shù)據(jù)是怎樣存放到SRAM中的?
沒(méi)有DMA
1.如果沒(méi)有DMA,CPU傳輸數(shù)據(jù)還要以內(nèi)核作為中轉(zhuǎn)站,比如要將ADC采集的數(shù)據(jù)轉(zhuǎn)移到到SRAM中,這個(gè)過(guò)程是這樣的:
內(nèi)核通過(guò)DCode經(jīng)過(guò)總線矩陣協(xié)調(diào),從獲取AHB存儲(chǔ)的外設(shè)ADC采集的數(shù)據(jù),
然后內(nèi)核再通過(guò)DCode經(jīng)過(guò)總線矩陣協(xié)調(diào)把數(shù)據(jù)存放到內(nèi)存SRAM中。
在這里插入圖片描述
有DMA傳輸
有DMA的話,
DMA傳輸時(shí)外設(shè)對(duì)DMA控制器發(fā)出請(qǐng)求。
DMA控制器收到請(qǐng)求,觸發(fā)DMA工作。
DMA控制器從AHB外設(shè)獲取ADC采集的數(shù)據(jù),存儲(chǔ)到DMA通道中
DMA控制器的DMA總線與總線矩陣協(xié)調(diào),使用AHB把外設(shè)ADC采集的數(shù)據(jù)經(jīng)由DMA通道存放到SRAM中,這個(gè)數(shù)據(jù)的傳輸過(guò)程中,完全不需要內(nèi)核的參與,也就是不需要CPU的參與,
在這里插入圖片描述
我們把上面的步驟專業(yè)一點(diǎn)介紹:
在發(fā)生一個(gè)事件后,外設(shè)向DMA控制器發(fā)送一個(gè)請(qǐng)求信號(hào)。DMA控制器根據(jù)通道的優(yōu)先權(quán)處理請(qǐng)求。當(dāng)DMA控制器開(kāi)始訪問(wèn)發(fā)出請(qǐng)求的外設(shè)時(shí),DMA控制器立即發(fā)送給它一個(gè)應(yīng)答信號(hào)。當(dāng)從DMA控制器得到應(yīng)答信號(hào)時(shí),外設(shè)立即釋放它的請(qǐng)求。一旦外設(shè)釋放了這個(gè)請(qǐng)求,DMA控制器同時(shí)撤銷應(yīng)答信號(hào)。DMA傳輸結(jié)束,如果有更多的請(qǐng)求時(shí),外設(shè)可以啟動(dòng)下一個(gè)周期。
總之,每次DMA傳送由3個(gè)操作組成:
從外設(shè)數(shù)據(jù)寄存器或者從當(dāng)前外設(shè)/存儲(chǔ)器地址寄存器指示的存儲(chǔ)器地址取數(shù)據(jù),第一次傳輸時(shí)的開(kāi)始地址是DMA_CPARx或DMA_CMARx寄存器指定的外設(shè)基地址或存儲(chǔ)器單元;
存數(shù)據(jù)到外設(shè)數(shù)據(jù)寄存器或者當(dāng)前外設(shè)/存儲(chǔ)器地址寄存器指示的存儲(chǔ)器地址,第一次傳輸時(shí)的開(kāi)始地址是DMA_CPARx或DMA_CMARx寄存器指定的外設(shè)基地址或存儲(chǔ)器單元;
執(zhí)行一次DMA_CNDTRx寄存器的遞減操作,該寄存器包含未完成的操作數(shù)目。
DMA傳輸方式
方法1:DMA_Mode_Normal,正常模式,
當(dāng)一次DMA數(shù)據(jù)傳輸完后,停止DMA傳送 ,也就是只傳輸一次 方法2:DMA_Mode_Circular ,循環(huán)傳輸模式
當(dāng)傳輸結(jié)束時(shí),硬件自動(dòng)會(huì)將傳輸數(shù)據(jù)量寄存器進(jìn)行重裝,進(jìn)行下一輪的數(shù)據(jù)傳輸。也就是多次傳輸模式
仲裁器
仲裁器的作用是確定各個(gè)DMA傳輸?shù)膬?yōu)先級(jí)
仲裁器根據(jù)通道請(qǐng)求的優(yōu)先級(jí)來(lái)啟動(dòng)外設(shè)/存儲(chǔ)器的訪問(wèn)。
優(yōu)先權(quán)管理分2個(gè)階段:
軟件:每個(gè)通道的優(yōu)先權(quán)可以在DMA_CCRx寄存器中設(shè)置,有4個(gè)等級(jí):
最高優(yōu)先級(jí)
高優(yōu)先級(jí)
中等優(yōu)先級(jí)
低優(yōu)先級(jí);
硬件:如果2個(gè)請(qǐng)求有相同的軟件優(yōu)先級(jí),則較低編號(hào)的通道比較高編號(hào)的通道有較高的優(yōu)先權(quán)。比如:如果軟件優(yōu)先級(jí)相同,通道2優(yōu)先于通道4。
注意:在大容量產(chǎn)品和互聯(lián)型產(chǎn)品中,DMA1控制器擁有高于DMA2控制器的優(yōu)先級(jí)。
DMA數(shù)據(jù)流(僅存在于STM32F4 /M4 內(nèi)核上)
在設(shè)置了DMA的通道之后,還要選擇通道對(duì)應(yīng)外設(shè)的數(shù)據(jù)流
8 個(gè) DMA 控制器數(shù)據(jù)流都能夠提供源和目標(biāo)之間的單向傳輸鏈路。每個(gè)數(shù)據(jù)流配置后都可以執(zhí)行:● 常規(guī)類型事務(wù):存儲(chǔ)器到外設(shè)、外設(shè)到存儲(chǔ)器或存儲(chǔ)器到存儲(chǔ)器的傳輸?!?雙緩沖區(qū)類型事務(wù):使用存儲(chǔ)器的兩個(gè)存儲(chǔ)器指針的雙緩沖區(qū)傳輸(當(dāng) DMA 正在進(jìn)行自/至緩沖區(qū)的讀/寫(xiě)操作時(shí),應(yīng)用程序可以進(jìn)行至/自其它緩沖區(qū)的寫(xiě)/讀操作)。要傳輸?shù)臄?shù)據(jù)量(多達(dá) 65535)可以編程,并與連接到外設(shè) AHB 端口的外設(shè)(請(qǐng)求 DMA 傳輸)的源寬度相關(guān)。每個(gè)事務(wù)完成后,包含要傳輸?shù)臄?shù)據(jù)項(xiàng)總量的寄存器都會(huì)遞減。
DMA_SxCR 寄存器控制數(shù)據(jù)流到底使用哪一個(gè)通道,每個(gè)數(shù)據(jù)流有 8 個(gè)通道可 供選擇,每次只能選擇其中一個(gè)通道進(jìn)行 DMA 傳輸。接下來(lái),我們看看 DMA2 的各數(shù)據(jù)流通 道映射表,如表 28.1.1 所示:
DMA 傳輸通道
每個(gè)通道都可以在有固定地址的外設(shè)寄存器和存儲(chǔ)器地址之間執(zhí)行DMA傳輸。DMA傳輸?shù)臄?shù)據(jù) 量是可編程的,大達(dá)到65535。包含要傳輸?shù)臄?shù)據(jù)項(xiàng)數(shù)量的寄存器,在每次傳輸后遞減。
可編程的數(shù)據(jù)量:外設(shè)和存儲(chǔ)器的傳輸數(shù)據(jù)量可以通過(guò)DMA_CCRx寄存器中的PSIZE和MSIZE位編程。
指針遞增模式
根據(jù) DMA_SxCR 寄存器中 PINC 和 MINC 位的狀態(tài),外設(shè)和存儲(chǔ)器指針在每次傳輸后可以自動(dòng)向后遞增或保持常量。當(dāng)設(shè)置為增量模式時(shí),下一個(gè)要傳輸?shù)牡刂穼⑹乔耙粋€(gè)地址加上增量值
通過(guò)單個(gè)寄存器訪問(wèn)外設(shè)源或目標(biāo)數(shù)據(jù)時(shí),禁止遞增模式十分有用。
如果使能了遞增模式,則根據(jù)在 DMA_SxCR 寄存器 PSIZE 或 MSIZE 位中編程的數(shù)據(jù)寬度,下一次傳輸?shù)牡刂穼⑹乔耙淮蝹鬏數(shù)牡刂愤f增 1個(gè)數(shù)據(jù)寬度、2個(gè)數(shù)據(jù)寬度或 4個(gè)數(shù)據(jù)寬度。
存儲(chǔ)器到存儲(chǔ)器模式
DMA通道的操作可以在沒(méi)有外設(shè)請(qǐng)求的情況下進(jìn)行,這種操作就是存儲(chǔ)器到存儲(chǔ)器模式。
當(dāng)設(shè)置了DMA_CCRx寄存器中的MEM2MEM位之后,在軟件設(shè)置了DMA_CCRx寄存器中的EN位啟動(dòng)DMA通道時(shí),DMA傳輸將馬上開(kāi)始。當(dāng)DMA_CNDTRx寄存器變?yōu)?時(shí),DMA傳輸結(jié)束。存儲(chǔ)器到存儲(chǔ)器模式不能與循環(huán)模式同時(shí)使用。
這里要注意僅 DMA2 的外設(shè)接口可以訪問(wèn)存儲(chǔ)器,所以僅 DMA2 控制器支持存儲(chǔ)器到存儲(chǔ)器的傳輸,DMA1 不支持。
存儲(chǔ)器到存儲(chǔ)器模式不能與循環(huán)模式同時(shí)使用。
DMA中斷
每個(gè)DMA通道都可以在DMA傳輸過(guò)半、傳輸完成和傳輸錯(cuò)誤時(shí)產(chǎn)生中斷。為應(yīng)用的靈活性考慮,通過(guò)設(shè)置寄存器的不同位來(lái)打開(kāi)這些中斷。
使沒(méi)開(kāi)啟,我們也可以通過(guò)查詢這些位來(lái)獲得當(dāng)前 DMA 傳輸?shù)臓顟B(tài)。這里我們常用的是 TCIFx位,即數(shù)據(jù)流 x 的 DMA 傳輸完成與否標(biāo)志。
可編程的數(shù)據(jù)傳輸寬度、對(duì)齊方式和數(shù)據(jù)大小端 當(dāng)PSIZE和MSIZE不相同時(shí),DMA模塊按照下圖進(jìn)行數(shù)據(jù)對(duì)齊。
注意:在大容量產(chǎn)品中, DMA2 通道 4 和 DMA2 通道 5 的中斷被映射在同一個(gè)中斷向量上。在互聯(lián)型產(chǎn)品 中, DMA2 通道 4 和 DMA2 通道 5 的中斷分別有獨(dú)立的中斷向量。所有其他的 DMA 通道都有自己的 中斷向量
DMA的內(nèi)存占用
在STM32控制器中,芯片采用Cortex-MX架構(gòu),總線結(jié)構(gòu)有了很大的優(yōu)化,DMA占用另外的地址總線,并不會(huì)與CPU的系統(tǒng)總線發(fā)生沖突。也就是說(shuō),DMA的使用不會(huì)影響CPU的運(yùn)行速度
但是要注意:DMA 控制器和Cortex-M3核共享系統(tǒng)數(shù)據(jù)總線執(zhí)行直接存儲(chǔ)器數(shù)據(jù)傳輸。當(dāng)CPU和DMA同時(shí)訪問(wèn)相同的目標(biāo)(RAM或外設(shè))時(shí),DMA請(qǐng)求可能會(huì)停止 CPU訪問(wèn)系統(tǒng)總線達(dá)若干個(gè)周期,總線仲裁器執(zhí)行循環(huán)調(diào)度,以保證CPU至少可以得到一半的系統(tǒng)總線(存儲(chǔ)器或外設(shè))帶寬。
DMA配置部分
此部分我們分為DMA寄存器和DMA庫(kù)函數(shù)分別介紹:
DMA寄存器
DMA配置參數(shù)包括:通道地址、優(yōu)先級(jí)、數(shù)據(jù)傳輸方向、存儲(chǔ)器/外設(shè)數(shù)據(jù)寬度、存儲(chǔ)器/外設(shè)地址是否增量、循環(huán)模式、數(shù)據(jù)傳輸量。
DMA中斷狀態(tài)寄存器(DMA_ISR)
我們?nèi)绻_(kāi)啟了 DMA_ISR 中這些中斷,在達(dá)到條件后就會(huì)跳到中斷服務(wù)函數(shù)里面去,即使 沒(méi)開(kāi)啟,我們也可以通過(guò)查詢這些位來(lái)獲得當(dāng)前 DMA 傳輸?shù)臓顟B(tài)。這里我們常用的是 TCIFx, 即通道 DMA 傳輸完成與否的標(biāo)志。
注意此寄存器為只讀寄存器,所以在這些位被置位之后,只 能通過(guò)其他的操作來(lái)清除。
DMA中斷標(biāo)志清除寄存器(DMA_IFCR)
DMA_IFCR 的各位就是用來(lái)清除 DMA_ISR 的對(duì)應(yīng)位的,通過(guò)寫(xiě) 0 清除。在 DMA_ISR 被置位后, 我們必須通過(guò)向該位寄存器對(duì)應(yīng)的位寫(xiě)入 0 來(lái)清除。
DMA通道x配置寄存器(DMA_CCRx)
該寄存器控制著 DMA 的很多相關(guān) 信息,包括數(shù)據(jù)寬度、外設(shè)及存儲(chǔ)器的寬度、通道優(yōu)先級(jí)、增量模式、傳輸方向、中斷允許、 使能等都是通過(guò)該寄存器來(lái)設(shè)置的。所以 DMA_CCRx 是 DMA 傳輸?shù)暮诵目刂萍拇嫫?/span>
DMA通道x傳輸數(shù)量寄存器(DMA_CNDTRx)(x = 1…7)
這個(gè)寄存器控制 DMA 通道 x 的每次 傳輸所要傳輸?shù)臄?shù)據(jù)量。其設(shè)置范圍為 0~65535。并且該寄存器的值會(huì)隨著傳輸?shù)倪M(jìn)行而減少, 當(dāng)該寄存器的值為 0 的時(shí)候就代表此次數(shù)據(jù)傳輸已經(jīng)全部發(fā)送完成了。所以可以通過(guò)這個(gè)寄存 器的值來(lái)知道當(dāng)前 DMA 傳輸?shù)倪M(jìn)度
DMA通道x外設(shè)地址寄存器(DMA_CPARx)(x = 1…7)
該寄存器用來(lái)存儲(chǔ) STM32 外設(shè)的地 址,比如我們使用串口 1,那么該寄存器必須寫(xiě)入 0x40013804(其實(shí)就是&USART1_DR)。如果使 用其他外設(shè),就修改成相應(yīng)外設(shè)的地址就行了。
DMA通道x配置寄存器(DMA_CMARx)
,該寄存器和 DMA_CPARx 差不多, 但是是用來(lái)放存儲(chǔ)器的地址的。比如我們使用 SendBuf[5200]數(shù)組來(lái)做存儲(chǔ)器,那么我們?cè)?DMA_CMARx 中寫(xiě)入&SendBuff 就可以了。
DMA寄存器配置流程
通道配置過(guò)程 下面是配置DMA通道x的過(guò)程(x代表通道號(hào)):
在DMA_CPARx寄存器中設(shè)置外設(shè)寄存器的地址。發(fā)生外設(shè)數(shù)據(jù)傳輸請(qǐng)求時(shí),這個(gè)地址將 是數(shù)據(jù)傳輸?shù)脑椿蚰繕?biāo)。
在DMA_CMARx寄存器中設(shè)置數(shù)據(jù)存儲(chǔ)器的地址。發(fā)生外設(shè)數(shù)據(jù)傳輸請(qǐng)求時(shí),傳輸?shù)臄?shù) 據(jù)將從這個(gè)地址讀出或?qū)懭脒@個(gè)地址。
在DMA_CNDTRx寄存器中設(shè)置要傳輸?shù)臄?shù)據(jù)量。在每個(gè)數(shù)據(jù)傳輸后,這個(gè)數(shù)值遞減。
在DMA_CCRx寄存器的PL[1:0]位中設(shè)置通道的優(yōu)先級(jí)。
在DMA_CCRx寄存器中設(shè)置數(shù)據(jù)傳輸?shù)姆较?、循環(huán)模式、外設(shè)和存儲(chǔ)器的增量模式、外 設(shè)和存儲(chǔ)器的數(shù)據(jù)寬度、傳輸一半產(chǎn)生中斷或傳輸完成產(chǎn)生中斷。
設(shè)置DMA_CCRx寄存器的ENABLE位,啟動(dòng)該通道。
一旦啟動(dòng)了DMA通道,它既可響應(yīng)連到該通道上的外設(shè)的DMA請(qǐng)求。當(dāng)傳輸一半的數(shù)據(jù)后,半傳輸標(biāo)志(HTIF)被置1,當(dāng)設(shè)置了允許半傳輸中斷位(HTIE)時(shí),將產(chǎn)生 一個(gè)中斷請(qǐng)求。在數(shù)據(jù)傳輸結(jié)束后,傳輸完成標(biāo)志(TCIF)被置1,當(dāng)設(shè)置了允許傳輸完成中斷位 (TCIE)時(shí),將產(chǎn)生一個(gè)中斷請(qǐng)求。
DMA庫(kù)函數(shù)
1.DMA初始化函數(shù)
DMA_DeInit(DMAX_ChannelX);
功能:將DMAyChannelx寄存器的初始化為其默認(rèn)值
注釋:RCC_ResetCmd中對(duì)DMA無(wú)定義,因此采用的直接操縱DMA寄存器的方式
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
功能:設(shè)置要開(kāi)啟的通道,還有一些參數(shù),包括外設(shè)基地址,存儲(chǔ)器基地址,傳輸?shù)臄?shù)據(jù)量,增量模式,數(shù)據(jù)寬度等。
具體看下方結(jié)構(gòu)體代碼介紹:
typedef struct { uint32_t DMA_PeripheralBaseAddr; /*設(shè)置DMA源地址*/ uint32_t DMA_MemoryBaseAddr; /*設(shè)置DMA目的地址*/ uint32_t DMA_DIR; /* 設(shè)置數(shù)據(jù)傳輸方向,決定是從外設(shè)讀取數(shù)據(jù)到內(nèi)存還送從內(nèi)存讀取數(shù) 據(jù)發(fā)送到外設(shè),也就是外設(shè)是源地還是目的地 */ uint32_t DMA_BufferSize; /*設(shè)置傳輸大小*/ uint32_t DMA_PeripheralInc; /*設(shè)置ReceiveBuff地址是否自增*/ uint32_t DMA_MemoryInc; /*設(shè)置傳輸數(shù)據(jù)時(shí)候內(nèi)存地址是否遞,需要開(kāi)啟*/ uint32_t DMA_PeripheralDataSize; /*外設(shè)的數(shù)據(jù)長(zhǎng)度是為字節(jié)傳輸(8bits),半 字傳輸(16bits) 還是字傳輸(32bits) */ uint32_t DMA_MemoryDataSize; /*設(shè)置內(nèi)存的數(shù)據(jù)長(zhǎng)度*/ uint32_t DMA_Mode; /*設(shè)置DMA的模式,正常模式/循環(huán)模式 是否循環(huán)發(fā)送*/ uint32_t DMA_Priority; /*設(shè)置 DMA 通道的優(yōu)先級(jí),有低,中,高,超高四種模式*/ uint32_t DMA_M2M; /*設(shè)置是否是存儲(chǔ)器到存儲(chǔ)器模式傳輸,*/ } DMA_InitTypeDef;
2.DMA使能函數(shù)
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
功能:使能或者失能DMA外設(shè)
例如:DMA_Cmd(DMA1_Channel1 , ENABLE); 3.DMA中斷使能函數(shù)
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);1
功能:配置指定的DMAy通道x的中斷
注釋:DMA_IT_TC:傳輸完成 DMA_IT_HT:傳輸一半 DMA_IT_TE:傳輸錯(cuò)誤
例如:DMA_ITConfig(DMA1_Channel1 , DMA_IT_TC , ENABLE);
4.設(shè)置CNDTRx和讀CNDTRx函數(shù) (通道傳輸數(shù)據(jù)量)
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);12
作用:前者設(shè)置DMA通道的傳輸數(shù)據(jù)量(DMA處于關(guān)閉狀態(tài));后者獲取當(dāng)前DMA通道傳輸剩余數(shù)據(jù)量(DMA處于開(kāi)啟狀態(tài))。
DMA庫(kù)函數(shù)配置過(guò)程:
使能DMA時(shí)鐘:RCC_AHBPeriphClockCmd();
初始化DMA通道:DMA_Init();
//設(shè)置通道;傳輸?shù)刂?;傳輸方向;傳輸?shù)據(jù)的數(shù)目;傳輸數(shù)據(jù)寬度;傳輸模式;優(yōu)先級(jí);是否開(kāi)啟存儲(chǔ)器到存儲(chǔ)器。
使能外設(shè)DMA;
以串口為例:使能串口DMA發(fā)送,串口DMA使能函數(shù)。調(diào)用函數(shù):USART_DMACmd();
使能DMA通道傳輸;函數(shù):DMA_Cmd();
查詢DMA傳輸狀態(tài)。函數(shù):DMA_GetFlagStatus();
獲取當(dāng)前剩余數(shù)據(jù)量大小 函數(shù):DMA_GetCurrDataCounter(DMA1_Channel4);
UART DMA傳輸
DMA就是一個(gè)搬運(yùn)工,可以將數(shù)據(jù)從一個(gè)位置搬運(yùn)到另一個(gè)位置。以UART為例,如果要接收數(shù)據(jù),會(huì)觸發(fā)UART中斷,然后CPU介入,在中斷中通過(guò)CPU將UART輸入寄存器的值讀出來(lái),存放到內(nèi)存中;而DMA方式,產(chǎn)生UART中斷后,DMA直接參與,把UART輸入寄存器的值搬運(yùn)到內(nèi)存中,CPU只需要在去檢查內(nèi)存的值就好了,這樣提高了CPU的效率。
DMA代碼配置
① DMA初始化配置
void dma_init(){ DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); /*DMA配置*/ DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口數(shù)據(jù)寄存器地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //內(nèi)存地址(要傳輸?shù)淖兞康闹羔? DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(從內(nèi)存到外設(shè)) DMA_InitStructure.DMA_BufferSize = 500; //傳輸內(nèi)容的大小 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è)數(shù)據(jù)單位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte ; //內(nèi)存數(shù)據(jù)單位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //DMA模式:一次傳輸,循環(huán) DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //優(yōu)先級(jí):高 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止內(nèi)存到內(nèi)存的傳輸 DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道 DMA_Cmd(DMA1_Channel4,ENABLE); DMA_SetCurrDataCounter(DMA_CH4,DMA1_MEM_LEN);//DMA通道的DMA緩存的大小 DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA發(fā)送完成后產(chǎn)生中斷 }
DMA中斷
void DMA1_Channel4_IRQHandler(void){ if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET) { DMA_ClearFlag(DMA1_FLAG_TC4); } }
main函數(shù)
#define SEND_BUF_SIZE 500 //發(fā)送數(shù)據(jù)長(zhǎng)度,最好等于sizeof(TEXT_TO_SEND)+2的整數(shù)倍. u8 SendBuff[SEND_BUF_SIZE]; //發(fā)送數(shù)據(jù)緩沖區(qū)const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串口實(shí)驗(yàn)"}; uint16_t i;int main(void){ uart_init(115200); //串口初始化為115200 for(i=0;i<500;i++) { SendBuff[i] =0xaf; } USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口dma傳輸 while(1); }
來(lái)源:嵌入式Linux
原文來(lái)源于:程序喵大人版權(quán)歸原作者或平臺(tái)所有,僅供學(xué)習(xí)參考與學(xué)術(shù)研究,如有侵權(quán),麻煩聯(lián)系刪除~感謝
評(píng)論