奚圣鑫 王宜懷 李躍華
1(蘇州大學(xué)計算機(jī)科學(xué)與技術(shù)學(xué)院 江蘇 蘇州 215000) 2(南通大學(xué)信息科學(xué)與技術(shù)學(xué)院 江蘇 南通 226019)
直接存儲器訪問(DMA)是一種數(shù)據(jù)傳輸?shù)姆绞絒1],它可以將數(shù)據(jù)不經(jīng)過CPU直接從一個地址空間復(fù)制到另一個地址空間,提供在外設(shè)和存儲器之間或者存儲器與存儲器之間的高速傳輸。在嵌入式微處理器的實際應(yīng)用過程中,無時無刻都需要數(shù)據(jù)的傳輸,我們正常的方法都是查詢方式進(jìn)行編程來控制I/O的輸入輸出,或者通過中斷來處理數(shù)據(jù)傳輸[2]。雖然采取中斷來控制I/O比查詢方式控制I/O更加有效和省時,但是仍然需要CPU的干預(yù)才能實現(xiàn)存儲器與I/O模塊之間的數(shù)據(jù)進(jìn)行傳輸。任何I/O設(shè)備與存儲器間的數(shù)據(jù)傳輸必須通過CPU,因此大大降低了CPU的效率。這兩種形式的I/O存在以下兩種缺點:(1) I/O數(shù)據(jù)傳輸?shù)乃俣仁芴幚砥餍阅芎虸/O設(shè)備所提供服務(wù)速度的限制;(2) 處理器負(fù)責(zé)控制I/O數(shù)據(jù)的傳輸時必須要執(zhí)行一些指令,這就浪費了CPU的時間。為了彌補(bǔ)這兩種數(shù)據(jù)傳輸方式的缺點,可以采用嵌入式處理器中的DMA數(shù)據(jù)傳輸方式。當(dāng)使用DMA傳輸數(shù)據(jù)時,這個動作本身是由DMA控制器(DMAC)來實現(xiàn)和完成的,無須CPU的介入和控制,也就不需要CPU先把所有數(shù)據(jù)復(fù)制到暫存器,然后把它們再寫回到目的地址去[3]。同時也沒有中斷處理I/O方式那樣需要保留現(xiàn)場和傳輸完成后的恢復(fù)現(xiàn)場的過程。DMA方式的數(shù)據(jù)傳輸與查詢方式訪問I/O和中斷驅(qū)動I/O相比,具有傳輸速度快、I/O響應(yīng)時間短、CPU額外開銷小的明顯優(yōu)點[4]?,F(xiàn)在越來越多的嵌入式微處理器都具有DMA技術(shù)以提供外設(shè)和存儲器之間的高速數(shù)據(jù)傳輸,但卻很少會去使用它。本文將基于STM32L431RC芯片來介紹DMA控制器的基本原理及技術(shù),在此基礎(chǔ)上提供一種DMA構(gòu)建的封裝方式,并將其與UART結(jié)合提供具體的應(yīng)用實例。
STM32中的DMA控制器(DMAC)包含2個DMA端口(DMA1,DMA2),每個端口都擁有7個通道,每個通道都可以執(zhí)行DMA傳輸。其傳輸數(shù)據(jù)量是可編程的,最大可以達(dá)到65 535[5]。DMAC和STM32核心共享系統(tǒng)的數(shù)據(jù)總線,在DMA傳輸時,DMA請求會暫停CPU訪問系統(tǒng)總線若干個周期,此階段由DMAC直接掌管總線,因此,存在著一個總線控制權(quán)轉(zhuǎn)移過程。此過程是由總線仲裁器執(zhí)行循環(huán)調(diào)度,以保證CPU至少可以得到一半的系統(tǒng)總線(存儲器或外設(shè))帶寬[6]。另外每個端口還包含一個仲裁器、中斷接口和內(nèi)部一些寄存器組,其結(jié)構(gòu)如圖1所示[7]。
圖1 DMAC結(jié)構(gòu)
DMAC可以將數(shù)據(jù)從源地址搬移到目的地址,在STM32中DMA操作存在三種操作模式。即存儲器到外設(shè)、外設(shè)到存儲器、存儲器到存儲器。一般的DMAC至少應(yīng)該具備以下的基本功能:
(1) 能夠接收外部設(shè)備發(fā)出的DMA請求,并向CPU提出總線占用請求。
(2) 在CPU對DMAC配置結(jié)束后,DMAC可以代替CPU對總線進(jìn)行控制。
(3) DMAC可以獨立地確定傳輸數(shù)據(jù)的起始地址和目的地址以及傳輸長度。
(4) DMA傳輸過程中,在發(fā)生傳輸錯誤時需要進(jìn)行報錯處理。需要獨立判斷是否傳輸成功,并且在傳輸成功后能夠發(fā)出中斷信號,釋放對總線的控制權(quán)。
當(dāng)然對于功能復(fù)雜的DMAC,除了完成以上功能之外,還會有其他的功能。一般的DMAC在傳遞完成一次連續(xù)地址的數(shù)據(jù)之后需要重新進(jìn)行配置才能進(jìn)行下一次的數(shù)據(jù)傳輸,但是復(fù)雜的DMAC可以內(nèi)部增加邏輯加減,在傳輸數(shù)據(jù)時無論有多少個非連續(xù)的數(shù)據(jù)塊,可以只配置一次即可完成。一次完整的DMA傳輸過程必須經(jīng)過請求、響應(yīng)、傳輸和結(jié)束四個過程,其工作流程如圖2所示。
圖2 DMA控制器工作流程
(1) CPU首先會將DMAC初始化。當(dāng)存儲設(shè)備I/O向DMAC發(fā)出請求DMA方式進(jìn)行數(shù)據(jù)傳輸請求。
(2) DMAC向CPU發(fā)出總線請求,然后CPU釋放總線控制權(quán),并通過DMAC通知I/O接口開始DMA傳輸。
“雅頌”之聲被古人視為“正聲”“正體”[11]?!吨茼灐肥侨祟惱硇跃耖_始自覺的產(chǎn)物,雖然雜糅著宗教情緒,但是非理性色彩并不濃郁。與《商頌》的“雅頌”思想擁有崇奉上帝的迷狂觀念不同,《周頌》的“雅頌”思想具有倫理化特征。
(3) DMAC獲得總線控制權(quán)后,即可進(jìn)行數(shù)據(jù)傳輸。整個數(shù)據(jù)傳輸過程主要可以分為兩個階段,分別為從源地址中讀取數(shù)據(jù)和寫回數(shù)據(jù)到目的地址。讀操作階段數(shù)據(jù)從源地址中被搬運(yùn)到數(shù)據(jù)總線上,然后再存放到DMAC內(nèi)部FIFO中用來緩存數(shù)據(jù)。寫操作是將FIFO中的緩存數(shù)據(jù)寫到目的地址上去。DMAC在同一時刻只能進(jìn)行一種操作。
(4) 當(dāng)完成數(shù)據(jù)傳輸后,DMAC立即釋放總線控制權(quán),至此,整個DMA操作結(jié)束。
DMAC中每個通道都有相應(yīng)的DMA配置寄存器(DMA_CCRx[1,2,…,7])、外設(shè)地址寄存器(DMA_CPARx[1,2,…,7])、存儲器地址寄存器(DMA_CMARx[1,2,…,7])、傳輸數(shù)據(jù)數(shù)量寄存器(DMA_CNDTRx[1,2,…,7]),此外還有DMA中斷狀態(tài)寄存器(DMA_ISR)和中斷標(biāo)志清理寄存器(DMA_IFCR)。每個寄存器的功能如表1所示。
表1 DMA寄存器列表
其中最重要的就是DMA配置寄存器,在使用DMA傳輸之前必須要對其進(jìn)行初始化。15到31位保留,其余各位的含義如表2所示。
表2 DMA配置寄存器各位表
外設(shè)地址存儲器指定當(dāng)傳輸時數(shù)據(jù)的地址是外設(shè)時的地址;存儲器地址寄存器指定當(dāng)傳輸時數(shù)據(jù)的地址是存儲器時的地址。傳輸數(shù)量寄存器指定傳輸次數(shù),每一次“先讀后寫”傳輸后遞減,直至為0傳輸結(jié)束,但是如果在配置寄存器中設(shè)置為循環(huán)模式時會自動再加載之前的設(shè)定值。
軟件構(gòu)件技術(shù)的出現(xiàn),為實現(xiàn)軟件構(gòu)件的工業(yè)化生產(chǎn)提供了理論與技術(shù)基石[8]。軟件構(gòu)件的封裝性、可移植性和可復(fù)用性是軟件構(gòu)件的基本特性,采用構(gòu)件技術(shù)設(shè)計軟件,可以使軟件具有更好的開放性、通用性和適應(yīng)性。在此思想基礎(chǔ)上,以STM32L431芯片為基礎(chǔ),提出DMA構(gòu)件基礎(chǔ)功能封裝規(guī)則。
首先對DMA進(jìn)行要點分析,即分析應(yīng)該設(shè)計哪幾個函數(shù)及函數(shù)入口參數(shù)。前面已經(jīng)分析了DMA傳輸數(shù)據(jù)的完整步驟,所以通用的DMA構(gòu)件必須封裝DMA初始化函數(shù)、傳輸數(shù)據(jù)函數(shù)、關(guān)閉DMA函數(shù)。DMA構(gòu)件由dma.h和dma.c兩個文件組成,如果想要使用,只需要將這兩個文件加入到工程項目中即可。
1) DMA模塊初始化(DMA_Init)。使用DMA功能之前必須對其進(jìn)行初始化,所以初始化函數(shù)必須提供。由于DMA端口有兩個,每個端口有7個通道,且DMA傳輸模式有三種,是否循環(huán)輸入。因此DMA初始化函數(shù)的參數(shù)為DMA端口、通道數(shù)、傳輸方向、是否循環(huán)輸入。這樣DMA初始化原型可以設(shè)計為:
2) DMA傳輸開始(DMA_Start)。在DMA初始化之后就可以開始DMA傳輸,此時需要傳輸數(shù)據(jù)的源地址、目的地址、數(shù)據(jù)長度,所以參數(shù)必須要有這三個,同時開始函數(shù)還要對DMA模塊進(jìn)行使能,只有使能之后才能正常使用。這樣DMA傳輸開始原型可以設(shè)計為:
void DMA_Start(uint32_t SrcAddr,uint32_t DstAddr,uint32_t Length)
3) DMA模塊關(guān)閉(DMA_DeInit)。在使用DMA傳輸數(shù)據(jù)之后,要將DMA模塊關(guān)閉,反使能模塊,重啟傳輸數(shù)據(jù)寄存器以及清除所有的包含中斷在內(nèi)的標(biāo)志位,所以此函數(shù)不需要傳遞參數(shù),原型可以直接設(shè)計為:
void DMA_DeInit()
這三個函數(shù)基本滿足了對DMA操作的基本需求。還有中斷使能與禁止等函數(shù)可以根據(jù)需求添加和使用。
在軟硬件的調(diào)試過程中,經(jīng)常需要將數(shù)據(jù)通過串口打印出來。使用串口進(jìn)行傳輸數(shù)據(jù),這時就可以利用DMA技術(shù)進(jìn)行數(shù)據(jù)傳輸。STM32L431RC芯片不僅提供了DMA基本功能,還提供了DMA請求復(fù)用器(DMAMUX)。請求復(fù)用器可以在外設(shè)和DMA控制器之間重新配置DMA請求[9],從而實現(xiàn)數(shù)據(jù)直接從存儲器到串口UART、SPI或者I2C等外設(shè),也可以從一個外設(shè)到另一個外設(shè)。
本文提供了DMA和串口UART復(fù)用的應(yīng)用實例,可以直接通過串口將微控制器內(nèi)存內(nèi)數(shù)據(jù)直接發(fā)送給上位機(jī),從而用來模擬我們?nèi)粘I钪械膒rintf函數(shù)的功能。printf函數(shù)有個缺陷,就是花費的時間太多了,雖然在日常使用中我們絕大部分人沒有發(fā)現(xiàn),但是在一些大型的項目對數(shù)據(jù)需要大量傳輸時,不能每次都讓微控制器等待來顯示串口,所以作者便考慮到能否用DMA來取代printf。但是用DMA來執(zhí)行串口的數(shù)據(jù)打印實際時間點是會比使用printf的時間點會晚個幾毫秒,因為DMA傳輸數(shù)據(jù)是在CPU完成當(dāng)前時間周期之后把系統(tǒng)總線讓給DMAC然后才開始數(shù)據(jù)傳輸,但是printf傳輸數(shù)據(jù)是發(fā)起后立即執(zhí)行。但是這幾毫秒在我們實際應(yīng)用中對我們來說沒有任何影響,但是對CPU來說,DMA傳輸并沒有占用CPU,從而CPU可以運(yùn)行更多的計算。DMA和串口UART復(fù)用的函數(shù)流程如圖3所示。
圖3 DMA串口復(fù)用函數(shù)流程
程序?qū)崿F(xiàn)了兩種串口傳輸以存儲器為起始地址的28個字節(jié)的數(shù)據(jù)到上位機(jī)上。其核心代碼如下:
int main(void)
{
uint8_t SourceData[30]=″this is a dma Sample program″;
//起始地址存放源數(shù)據(jù)
gpio_init(TIME_GPIO,1,1);
//初始化GPIO引腳
gpio_set(TIME_GPIO,0);
//GPIO輸出低電平
DMA_UART(&SourceData,UART_Debug,28);
//DMA串口傳輸數(shù)據(jù)
gpio_set(TIME_GPIO,1);
//GPIO輸出高電平
delay(1000);
//延時1 s
gpio_set(TIME_GPIO,0);
//GPIO輸出低電平
printf(SourceData);
//printf傳輸數(shù)據(jù)
gpio_set(TIME_GPIO,1);
//GPIO輸出高電平
}
經(jīng)反復(fù)通過對TIME_GPIO引腳高低電平狀態(tài)持續(xù)時間測試得出,在使用DMA復(fù)用串口發(fā)送一個包含了三十個字符的字符串只需要大約25 μs的CPU耗時,而printf則需要大約800 μs的CPU耗時。
本文實現(xiàn)了在STM32L431的微控制器上的DMA數(shù)據(jù)傳輸,并且發(fā)現(xiàn)在結(jié)合UART、SPI或I2C等設(shè)備時,會比我們正常使用這些設(shè)備更加方便。因為我們在使用這些設(shè)備的同時CPU會花費大量的時間在數(shù)據(jù)的傳輸上,如果我們采用了DMA來進(jìn)行數(shù)據(jù)傳輸,則會大量減少CPU的工作任務(wù),從而讓CPU去執(zhí)行其他工作。實踐證明,在利用基于DMA共性技術(shù)的基礎(chǔ)上封裝的DMA構(gòu)件,可以將繁重的數(shù)據(jù)傳輸工作通過DMA控制器來完成,從而提高了CPU的數(shù)據(jù)處理能力和CPU的工作效率。