DMA是直接存儲(chǔ)器訪問(wèn)(DirectMemory Access)的縮寫。在MCU芯片中,DMA是除CPU之外,最常見(jiàn)的總線主設(shè)備。作為總線主設(shè)備,DMA控制器可以輸出地址和控制信號(hào)到總線上,主動(dòng)地發(fā)起和控制數(shù)據(jù)傳輸過(guò)程,它能夠按照程序的配置,在兩個(gè)從設(shè)備之間傳輸數(shù)據(jù)。例如在存儲(chǔ)器和I2C模塊之間傳輸數(shù)據(jù),實(shí)現(xiàn)I2C數(shù)據(jù)的發(fā)送或接收,或從ADC讀出數(shù)據(jù)再傳送到USART進(jìn)行發(fā)送。
下圖是LPC82x的部分框圖,圖中醒目標(biāo)出了總線主設(shè)備,和DMA控制器。? ? ? ? ? ? ? ? ? ?? ??圖1.LPC82x結(jié)構(gòu)框圖(部分)在LPC82x的所有片內(nèi)外設(shè)中,只有DMA控制器是總線主設(shè)備,其它都是總線從設(shè)備,只有主設(shè)備才能主動(dòng)發(fā)起數(shù)據(jù)的傳輸操作。
DMA的優(yōu)勢(shì)是,可以在CPU最少的干預(yù)下,高效地執(zhí)行數(shù)據(jù)塊的傳輸,節(jié)省CPU的時(shí)間,同時(shí)可以在CPU執(zhí)行內(nèi)部操作而不訪問(wèn)總線時(shí),更高效地利用總線的時(shí)間。
1.1DMA控制器的一些基本操作在介紹LPC800的DMA控制器之前,先通過(guò)這個(gè)傳輸示意圖,回顧一下通用DMA控制器必須具備的基本操作。
圖2.DMA傳輸示意圖
■產(chǎn)生數(shù)據(jù)傳輸?shù)脑吹刂泛湍繕?biāo)地址:DMA控制器在接到傳輸請(qǐng)求后,在內(nèi)部總線上產(chǎn)生源數(shù)據(jù)地址SA,讀出要傳輸?shù)臄?shù)據(jù),然后再產(chǎn)生存放數(shù)據(jù)的目標(biāo)地址DA,將數(shù)據(jù)寫入指定的地方。
■控制每次傳輸后地址的變化:可以控制每次DMA傳輸是涉及到一個(gè)連續(xù)的地址區(qū)域還是單個(gè)獨(dú)立的地址。每次讀寫的源地址和目標(biāo)地址分別改變或不改變,也可以同步地改變。
■控制傳輸?shù)臄?shù)據(jù)長(zhǎng)度:軟件需要指定每次DMA傳輸?shù)臄?shù)據(jù)數(shù)量n。
■指定傳輸?shù)臄?shù)據(jù)寬度:軟件需要指定每次DMA讀寫的數(shù)據(jù)寬度,一般是以內(nèi)部數(shù)據(jù)總線的寬度為限。對(duì)于32位MCU,可以是1個(gè)字節(jié)、2個(gè)字節(jié)(半字)或4個(gè)字節(jié)(字)。
■控制傳輸數(shù)據(jù)的節(jié)奏,即傳輸數(shù)據(jù)的時(shí)機(jī):每次DMA讀寫都需要在有傳輸請(qǐng)求時(shí)才會(huì)執(zhí)行。傳輸請(qǐng)求可以來(lái)自于數(shù)據(jù)源設(shè)備,例如ADC轉(zhuǎn)換結(jié)束;傳輸請(qǐng)求也可以來(lái)自于數(shù)據(jù)目標(biāo)設(shè)備,例如SPI的發(fā)送就緒。因此每?jī)纱蝹鬏斦?qǐng)求的間隔可以不一致。DMA的傳輸請(qǐng)求也可以由DMA控制器內(nèi)部產(chǎn)生,用于內(nèi)存中數(shù)據(jù)塊的傳送。
■狀態(tài)查詢和中斷控制:DMA控制器的狀態(tài)和中斷可以是多種多樣,通常有傳輸開(kāi)始、傳輸結(jié)束、傳輸錯(cuò)誤等。
LPC800的DMA控制器實(shí)現(xiàn)了上述所有的基本控制功能,而且還有不少自己的特色,下面一一介紹。
1.2DMA傳輸與CPU指令的執(zhí)行不管是CPU還是DMA控制器,都要通過(guò)同一條總線訪問(wèn)存儲(chǔ)器和各種片內(nèi)外設(shè),進(jìn)行數(shù)據(jù)傳輸。CPU的基本操作就是取指、譯碼、運(yùn)算、執(zhí)行的過(guò)程,取指操作需要占用總線,執(zhí)行階段的讀數(shù)據(jù)或?qū)憯?shù)據(jù)操作也需要占用總線。DMA控制器可以充分地利用CPU不占用總線的時(shí)間,在總線上傳輸數(shù)據(jù)。
如果在同一個(gè)時(shí)間,CPU和DMA控制器都需要占用總線,這種情況下需要有仲裁機(jī)制,協(xié)調(diào)兩個(gè)總線主設(shè)備的動(dòng)作。
如果在總線已經(jīng)被某個(gè)主設(shè)備(例如CPU)占用的時(shí)候,另一個(gè)主設(shè)備(例如DMA控制器)就會(huì)稍作等待,待總線空閑時(shí),再開(kāi)始數(shù)據(jù)傳輸。
從以上描述可以看出,DMA可以在不需CPU干預(yù)的情況下,利用CPU不占用總線的空閑時(shí)間進(jìn)行數(shù)據(jù)傳輸。這樣不但提高了總線的利用率,還減輕了CPU搬運(yùn)數(shù)據(jù)的負(fù)擔(dān),提高了系統(tǒng)的并行性,能夠?qū)崿F(xiàn)更復(fù)雜的控制要求,或降低整體的功耗。
1.3LPC800的DMA控制器LPC800的DMA控制器具有如下特性:▲多個(gè)通道,每個(gè)通道唯一地連接到一個(gè)片內(nèi)外設(shè)的輸入或輸出請(qǐng)求,例如USART、SPI和I2C等通信外設(shè)。▲DMA傳輸可以由片內(nèi)或片外事件觸發(fā),每個(gè)DMA通道都可以有多個(gè)觸發(fā)輸入源,每次傳輸只能選擇一個(gè)觸發(fā)源。▲可以指定每個(gè)DMA通道的優(yōu)先級(jí),當(dāng)通道之間的傳輸需求發(fā)生沖突時(shí),高優(yōu)先級(jí)通道先進(jìn)行傳輸。▲傳輸描述符機(jī)制,通過(guò)多個(gè)傳輸描述符互聯(lián),可以實(shí)現(xiàn)鏈?zhǔn)降?/span>DMA傳輸控制。▲每次(每個(gè)傳輸描述符)最多可以傳輸1024個(gè)字(1024x4=4096字節(jié))。▲地址增量的多種選項(xiàng),允許靈活的數(shù)據(jù)包處理。LPC800各系列的DMA配置如下:
系列
|
通道數(shù)目
|
觸發(fā)輸入源數(shù)目
|
LPC80x、LPC81x
|
0
|
0
|
LPC82x
|
18
|
9
|
LPC83x
|
18
|
8
|
LPC84x
|
25
|
13
|
DMA控制器有15個(gè)寄存器,涉及到所有通道,可以分為四組。
寄存器組
|
寄存器名稱
|
功能
|
說(shuō)明
|
通用寄存器組
|
CTL
|
DMA控制器寄存器
|
只有一個(gè)控制位,使能DMA控制器
|
INTSTAT
|
中斷狀態(tài)寄存器
|
標(biāo)志是否有掛起的中斷
|
|
SRAMBASE
|
傳輸描述符地址寄存器
|
所有通道第一個(gè)傳輸描述符的存放地址(必須512字節(jié)對(duì)齊)
|
|
通道控制寄存器組
|
ENABLESET0
|
通道使能寄存器
|
每個(gè)通道占用一位。表示是否使能對(duì)應(yīng)通道
|
ENABLECLR0
|
通道失能寄存器
|
每個(gè)通道占用一位。表示是否失能對(duì)應(yīng)通道
|
|
ACTIVE0
|
通道激活狀態(tài)寄存器
|
每個(gè)通道占用一位。表示對(duì)應(yīng)通道是否加載了傳輸描述符
|
|
BUSY0
|
通道忙狀態(tài)寄存器
|
每個(gè)通道占用一位。表示對(duì)應(yīng)通道是否正在搬運(yùn)數(shù)據(jù)
|
|
通道中斷寄存器組
|
ERRINT0
|
錯(cuò)誤中斷狀態(tài)寄存器
|
每個(gè)通道占用一位。表示是否有錯(cuò)誤中斷
|
INTENSET0
|
中斷使能寄存器
|
每個(gè)通道占用一位。表示是否使能對(duì)應(yīng)通道的中斷
|
|
INTENCLR0
|
中斷失能寄存器
|
每個(gè)通道占用一位。表示是否失能對(duì)應(yīng)通道的中斷
|
|
INTA0
|
中斷A狀態(tài)寄存器
|
每個(gè)通道占用一位。表示是否有中斷A
|
|
INTB0
|
中斷B狀態(tài)寄存器
|
每個(gè)通道占用一位。表示是否有中斷B
|
|
傳輸控制寄存器
|
SETVALID0
|
設(shè)置“有效”控制位寄存器
|
每個(gè)通道占用一位。用于設(shè)置描述符的“有效”控制位
|
SETTRIG0
|
設(shè)置“觸發(fā)”控制位寄存器
|
每個(gè)通道占用一位。用于觸發(fā)對(duì)應(yīng)的通道傳輸
|
|
ABORT0
|
通道中止傳輸寄存器
|
每個(gè)通道占用一位。用于中止對(duì)應(yīng)的通道傳輸
|
寄存器名稱
|
功能
|
說(shuō)明
|
CFG
|
通道配置寄存器
|
用于配置通道的使能、觸發(fā)、成組傳輸(Burst)和優(yōu)先級(jí)的選項(xiàng)
|
CTLSTAT
|
通道控制狀態(tài)寄存器
|
用于標(biāo)示通道的有效和觸發(fā)狀態(tài)
|
XFERCFG
|
通道傳輸配置寄存器
|
用于配置通道的各個(gè)配置選項(xiàng)
|
除了上述寄存器外,LPC800的DMA控制器通過(guò)位于內(nèi)存中的傳輸描述符,控制每次的DMA傳輸。每個(gè)傳輸描述符有4個(gè)字(32位/字),內(nèi)容如下:多個(gè)傳輸描述符可以構(gòu)成一個(gè)連續(xù)的鏈條或循環(huán)鏈,鏈條中的每一個(gè)描述符對(duì)應(yīng)一次DMA傳輸。傳輸?shù)臄?shù)據(jù)數(shù)量、數(shù)據(jù)寬度以及地址變化的方式等,由XFERCFG寄存器的內(nèi)容指定。
每次DMA傳輸開(kāi)始前,DMA控制器都要把一個(gè)完整的描述符讀入,傳輸描述符中偏移地址為0x0的字會(huì)被傳送到XFERCFG寄存器中,用于控制各項(xiàng)傳輸參數(shù)。一個(gè)鏈條的第一次傳輸參數(shù),需要由軟件直接寫入到XFERCFG寄存器,因此鏈條中的第一個(gè)描述符的第一個(gè)字為保留位。在傳輸開(kāi)始時(shí),DMA控制器會(huì)把傳輸區(qū)的地址讀入內(nèi)部寄存器中。
使用描述符的鏈接特性,可以方便地實(shí)現(xiàn)多種數(shù)據(jù)傳輸控制。
例如需要使用SPI驅(qū)動(dòng)一個(gè)LCD屏幕產(chǎn)生動(dòng)畫(huà)效果時(shí),可以配置為下圖所示的乒乓結(jié)構(gòu)的DMA描述符鏈條,CPU只需要不斷地生成顯示圖片,由DMA控制器平行地進(jìn)行圖片數(shù)據(jù)至LCD屏幕的傳送。
? ? ? ? ? ? ? ? ? ??圖3.乒乓結(jié)構(gòu)的DMA描述符鏈
這里使用了三個(gè)傳輸描述符。第一個(gè)描述符(鏈頭)指示將緩沖區(qū)A的數(shù)據(jù)傳輸?shù)?/span>SPI的發(fā)送寄存器,后面兩個(gè)描述符分別指示緩沖區(qū)B和緩沖區(qū)A的數(shù)據(jù),輪流傳輸?shù)?/span>SPI的發(fā)送寄存器。CPU只需要在對(duì)應(yīng)的緩沖區(qū)準(zhǔn)備好數(shù)據(jù),再設(shè)置對(duì)應(yīng)的描述符為“有效”,接下來(lái)DMA控制器就會(huì)直接把數(shù)據(jù)傳送到SPI模塊進(jìn)行發(fā)送。
1.3.3 DMA傳輸通道每個(gè)通信外設(shè)的發(fā)送傳輸可以產(chǎn)生DMA請(qǐng)求,接收傳輸也可以產(chǎn)生DMA請(qǐng)求。
LPC800的DMA控制器非常簡(jiǎn)單,每個(gè)片內(nèi)外設(shè)產(chǎn)生的DMA傳輸請(qǐng)求信號(hào),唯一地連接到一個(gè)固定的DMA通道。即如果把片內(nèi)外設(shè)作為DMA傳輸?shù)脑椿蚰繕?biāo),并且希望由該外設(shè)來(lái)控制傳輸?shù)墓?jié)奏(通過(guò)DMA傳輸請(qǐng)求信號(hào)),則必須使用對(duì)應(yīng)的通道。如果能夠使用其它的方法(例如時(shí)鐘觸發(fā)等),保證外設(shè)不會(huì)發(fā)生數(shù)據(jù)溢出的情況,則可以使用任意通道,但這種用法不能最優(yōu)地利用帶寬時(shí)間,除非需要特殊的時(shí)序控制,一般不建議使用。
每個(gè)通道對(duì)應(yīng)的DMA請(qǐng)求源如下表所示:
表1.DMA通道與DMA請(qǐng)求源的對(duì)應(yīng)表
1.3.4 DMA觸發(fā)源的選擇
在DMA控制器之外,有一個(gè)DMA觸發(fā)輸入 (DMA TRIGMUX)模塊,每個(gè)DMA通道都在這個(gè)模塊中有一個(gè)對(duì)應(yīng)的多選一選擇器,由DMA_ITRIG_INMUXn寄存器控制(n對(duì)應(yīng)表1的通道號(hào)),用戶可以在多種信號(hào)中選擇一個(gè)作為DMA的觸發(fā)信號(hào)。下表列出了所有可能的選項(xiàng):表2.DMA觸發(fā)選項(xiàng)(1):在LPC82x/83x中,n取值0~17;在LPC84x中,n取值0~24。
注*:每一個(gè)DMA通道都輸出一個(gè)觸發(fā)信號(hào),所有通道輸出的觸發(fā)信號(hào)都連接到兩個(gè)多選一的選擇器:DMA_INMUX_INMUX0和DMA_INMUX_INMUX1,這兩個(gè)多選一選擇器的輸出可以作為另一個(gè)DMA通道的觸發(fā)源選項(xiàng)之一。這個(gè)配置允許多個(gè)DMA通道的協(xié)同操作。
? ? ? ? ? ? ? ? ? ? ? ?圖4.DMA觸發(fā)輸入框圖
上圖為某個(gè)DMA通道的觸發(fā)輸入模塊框圖,每個(gè)通道都有一個(gè)這樣相同的電路用于選擇它的觸發(fā)輸入。 1.4 DMA傳輸?shù)恼?qǐng)求、觸發(fā)與生成傳輸概念每個(gè)通道的DMA觸發(fā)信號(hào)相當(dāng)于這個(gè)通道的總開(kāi)關(guān),只有打開(kāi)這個(gè)總開(kāi)關(guān),才能夠進(jìn)行隨后的DMA傳輸操作。每次DMA傳輸(即圖2示意中的每一次讀寫)的時(shí)機(jī),則由DMA請(qǐng)求信號(hào)決定。
成組傳輸(Burst)是指在一次觸發(fā)之后,按照指定的次數(shù)進(jìn)行一組數(shù)據(jù)的傳輸,這組數(shù)據(jù)傳輸完成后,需要另一次的觸發(fā)條件才能進(jìn)行下一組的數(shù)據(jù)傳輸。
成組傳輸控制和DMA傳輸請(qǐng)求控制組合為四種操作模式,他們與觸發(fā)信號(hào)的關(guān)系如下表所示。
表中的DMA傳輸請(qǐng)求信號(hào)以USART的TXRDY為例:
組合模式
|
使能成組傳輸(TRIGBURST,見(jiàn)1.5.1節(jié))
|
|||
使能外設(shè)請(qǐng)求(PERIPHREQEN,見(jiàn)1.5.1節(jié))
|
||||
操作模式說(shuō)明
|
||||
0
|
0
|
0
|
觸發(fā)信號(hào)用于啟動(dòng)完整的DMA傳輸過(guò)程,只需一個(gè)觸發(fā)信號(hào)即可完成所有數(shù)據(jù)傳輸。
|
DMA將以最快的速度,連續(xù)不斷地傳送數(shù)據(jù),直到完成所有數(shù)據(jù)。
|
1
|
0
|
1
|
每個(gè)DMA傳輸請(qǐng)求(TXRDY),只能傳輸一個(gè)數(shù)據(jù)。
|
|
2
|
1
|
0
|
每個(gè)觸發(fā)信號(hào)啟動(dòng)一組DMA傳輸,每組傳送BURSTPOWER(見(jiàn)1.5.1節(jié))個(gè)數(shù)據(jù)。因此總共需要(XFERCOUNT/BURSTPOWER)組的傳輸(即需要相同數(shù)目的觸發(fā)信號(hào)),才能完成所有數(shù)據(jù)。
XFERCOUNT位于XFERCFG寄存器(見(jiàn)1.5.2節(jié))。
|
DMA將以最快的速度,連續(xù)不斷地傳送數(shù)據(jù),直到完成所有數(shù)據(jù)。
|
3
|
1
|
1
|
每個(gè)DMA傳輸請(qǐng)求(TXRDY),只能傳輸一個(gè)數(shù)據(jù)。
|
表3.成組傳輸和外設(shè)請(qǐng)求與觸發(fā)信號(hào)的關(guān)系表
表中的組合模式0適合于從存儲(chǔ)器至存儲(chǔ)器的數(shù)據(jù)塊拷貝;組合模式1適合于常用的通信模塊的數(shù)據(jù)發(fā)送和接收。
組合模式2、3則視具體的應(yīng)用情況,由用戶自由發(fā)揮。例如,要在USART上發(fā)送若干個(gè)固定長(zhǎng)度的數(shù)據(jù)包,而發(fā)送每個(gè)數(shù)據(jù)包的時(shí)間需要由定時(shí)器來(lái)決定,則可以使用上述的組合模式3,設(shè)置BURSTPOWER為數(shù)據(jù)包的長(zhǎng)度,設(shè)置SCT定時(shí)器產(chǎn)生觸發(fā)信號(hào)(見(jiàn)表2的SCT_DMA0/1)。
拿自動(dòng)步槍做一個(gè)形象的比喻,成組的概念相當(dāng)于子彈夾,外設(shè)請(qǐng)求相當(dāng)于扳機(jī),觸發(fā)相當(dāng)于擊發(fā)保險(xiǎn),一次DMA傳輸中需要打出一箱子彈。那么每種組合模式有如下對(duì)應(yīng):■組合模式0:打開(kāi)擊發(fā)保險(xiǎn)后,不需其它動(dòng)作,整箱子彈即全部射出。■組合模式1:打開(kāi)擊發(fā)保險(xiǎn)后,每扣動(dòng)一次扳機(jī),射出一發(fā)子彈,直到打完整箱子彈。■組合模式2:打開(kāi)擊發(fā)保險(xiǎn)后,不需其它動(dòng)作,一個(gè)彈夾中的子彈即全部射出。然后再次打開(kāi)擊發(fā)保險(xiǎn),即射出另一個(gè)彈夾中的全部子彈。重復(fù)上述操作直到整箱子彈打光。■組合模式3:打開(kāi)擊發(fā)保險(xiǎn)后,每扣動(dòng)一次扳機(jī),射出一個(gè)彈夾中的一顆子彈,重復(fù)直到這個(gè)彈夾中的子彈打光,擊發(fā)保險(xiǎn)自動(dòng)關(guān)閉。然后須再次打開(kāi)擊發(fā)保險(xiǎn),再一次次地扣動(dòng)扳機(jī),逐個(gè)射出另一個(gè)彈夾中的所有子彈,擊發(fā)保險(xiǎn)再次自動(dòng)關(guān)閉。重復(fù)上述過(guò)程直到打完整箱子彈。
這里有一個(gè)要求,即XFERCOUNT必須能被BURSTPOWER整除,即一箱子彈的數(shù)目,必須是一個(gè)彈夾能容納子彈個(gè)數(shù)的倍數(shù)。
1.5 DMA通道參數(shù)寄存器
在1.3.1節(jié)中列出的寄存器,用于控制整個(gè)DMA控制器,以及控制每個(gè)通道的使能、中斷和觸發(fā)等狀態(tài)。每個(gè)通道的具體工作模式,由下述三個(gè)寄存器來(lái)描述。
1.5.1DMA通道配置寄存器(CFG)
各個(gè)控制域的說(shuō)明如下:▲PERIPHREQEN:使能外設(shè)請(qǐng)求。0 – 使能外設(shè)請(qǐng)求;1 – 不使能外設(shè)請(qǐng)求。
每個(gè)通道的外設(shè)請(qǐng)求來(lái)源是固定的,見(jiàn)表1。例如對(duì)應(yīng)SPI0的發(fā)送就緒信號(hào)(TXRDY)的DMA通道,在LPC82x/83x中是通道7,在LPC84x中是通道11。▲HWTRIGEN:使能硬件觸發(fā)。硬件觸發(fā)信號(hào)源由DMA_ITRIG_INMUXn寄存器選擇,見(jiàn)1.3.4節(jié)。0 – 使能硬件觸發(fā);1 – 不使能硬件觸發(fā)。▲TRIGPOL:觸發(fā)極性。▲TRIGTYPE:觸發(fā)類型。TRIGPOL和TRIGTYPE共同決定如何使用觸發(fā)信號(hào),組合關(guān)系如下表。
TRIGTYPE
|
TRIGPOL
|
說(shuō)明
|
0
|
0
|
下降沿觸發(fā)
|
0
|
1
|
上升沿觸發(fā)
|
1
|
0
|
低電平觸發(fā)
|
1
|
1
|
高電平觸發(fā)
|
0 – 觸發(fā)之后不按組傳輸(或可理解為所有數(shù)據(jù)為一組);1 - 觸發(fā)之后執(zhí)行成組傳輸。▲BURSTPOWER:成組傳輸中每組的長(zhǎng)度。這個(gè)域的內(nèi)容為2的冪次數(shù)值,取值為0~10,表示每組長(zhǎng)度為1(20)、2(21)、4(22) 、8(23)、……、1024(210)。不支持0~10之外的數(shù)值。
注意:XFERCOUNT必須是BURSTPOWER的倍數(shù)。
▲SRCBURSTWRAP:成組傳輸中每組傳輸結(jié)束后,是否需要恢復(fù)傳輸?shù)脑吹刂贰?/span>0 – 不恢復(fù)源地址;1 – 恢復(fù)源地址。這個(gè)控制項(xiàng)適合于重復(fù)地讀出相同的數(shù)據(jù)塊,或相同的一組寄存器。▲DSTBURSTWRAP:成組傳輸中每組傳輸結(jié)束后,是否需要恢復(fù)傳輸?shù)哪繕?biāo)地址。0 – 不恢復(fù)目標(biāo)地址;1 – 恢復(fù)目標(biāo)地址。
這個(gè)控制項(xiàng)適合于重復(fù)地寫入相同的存儲(chǔ)區(qū),例如重復(fù)地讀出一組傳感器的數(shù)值,軟件只關(guān)心即時(shí)的數(shù)值,而不關(guān)心數(shù)值變化的過(guò)程。▲CHPRIORITY:設(shè)置本通道的優(yōu)先級(jí)。在多個(gè)通道同時(shí)請(qǐng)求獲得總線進(jìn)行傳輸時(shí),優(yōu)先級(jí)高的通道先得到總線的使用權(quán)限。
0 – 最高優(yōu)先級(jí);7 – 最低優(yōu)先級(jí)。
1.5.2 DMA通道傳輸配置寄存器(XFERCFG)
該寄存器的內(nèi)容給出了當(dāng)前DMA傳輸?shù)母黜?xiàng)參數(shù)。一個(gè)傳輸描述符指定的一次傳輸結(jié)束后,DMA控制器會(huì)自動(dòng)地讀入鏈條中的下一個(gè)描述符,描述符的第一個(gè)字的內(nèi)容會(huì)加載到XFERCFG寄存器,見(jiàn)1.3.2節(jié)的說(shuō)明。
XFERCFG各個(gè)控制域的說(shuō)明如下:▲CFGVALID:表示所對(duì)應(yīng)的描述符是否有效。0 – 描述符無(wú)效;1 – 描述符有效。▲RELOAD:當(dāng)前描述符所指定的傳輸完成后,是否需要讀入下一個(gè)描述符。0 – 不讀入下一個(gè)描述符;1 – 讀入下一個(gè)描述符,允許描述符的鏈接操作。▲SWTRIG:軟件觸發(fā)。0 – 需要由HWTRIGEN、TRIGPOL和TRIGTYPE指定通道的觸發(fā)條件。
1 – 設(shè)置此位表示該通道的觸發(fā)條件立即滿足。
注意:使用軟件觸發(fā)時(shí),在TRIGBURST=0時(shí),不得使用電平觸發(fā)。▲CLRTRIG:當(dāng)前描述符的傳輸結(jié)束后,是否清除觸發(fā)條件。0 – 不清除。如果RELOAD=1,則下一個(gè)描述符的觸發(fā)條件滿足。
1 – 清除。當(dāng)前描述符指示的傳輸結(jié)束后,清除觸發(fā)條件。
注意:只有軟件觸發(fā)條件和邊沿觸發(fā)條件可以被清除,而電平觸發(fā)條件不能被清除。▲SETINTA:當(dāng)前描述符的傳輸結(jié)束后,是否產(chǎn)生中斷標(biāo)志INTA。0 – 不產(chǎn)生中斷標(biāo)志;1 – 產(chǎn)生中斷標(biāo)志。▲SETINTB:當(dāng)前描述符的傳輸結(jié)束后,是否產(chǎn)生中斷標(biāo)志INTB。0 – 不產(chǎn)生中斷標(biāo)志;1 – 產(chǎn)生中斷標(biāo)志。
INTA和INTB在硬件上沒(méi)有差別,用戶可以用這兩個(gè)中斷(標(biāo)志)區(qū)別是哪個(gè)描述符的傳輸完成了,尤其是在乒乓結(jié)構(gòu)的傳輸中。▲WIDTH:表示每次DMA傳輸?shù)臄?shù)據(jù)寬度。源地址的讀和目標(biāo)地址的寫,使用相同的數(shù)據(jù)寬度。0 – 8位數(shù)據(jù)傳輸;1 – 16位數(shù)據(jù)傳輸;2 – 32位數(shù)據(jù)傳輸;3 – 保留組合,不得使用。
注意:如果要求的數(shù)據(jù)寬度是16位或32位,則傳輸?shù)牡刂芬脖仨毞謩e是2字節(jié)或4字節(jié)對(duì)齊的。▲SRCINC:表示每次傳輸一個(gè)數(shù)據(jù)后,源地址的增量變化。0 – 地址無(wú)變化。
1 – 地址按數(shù)據(jù)寬度+1,指向數(shù)據(jù)區(qū)中的下一個(gè)數(shù)據(jù)。
2 – 地址按數(shù)據(jù)寬度+2。
3 – 地址按數(shù)據(jù)寬度+4。▲DSTINC:表示每次傳輸一個(gè)數(shù)據(jù)后,目標(biāo)地址的增量變化。該域的取值含義與SRCINC一樣。
SRCINC和DSTINC取值為0,最常見(jiàn)的應(yīng)用場(chǎng)景是面對(duì)外設(shè)寄存器的讀寫,例如傳送一個(gè)數(shù)據(jù)塊至SPI0的TXDAT,或從USART的RXDAT讀出一組數(shù)據(jù)至存儲(chǔ)區(qū)。
SRCINC和DSTINC取值為1,最常見(jiàn)的應(yīng)用場(chǎng)景對(duì)一個(gè)連續(xù)的存儲(chǔ)區(qū)的讀寫。
SRCINC和DSTINC取值為2或3時(shí),一個(gè)應(yīng)用案例是,當(dāng)WIDTH=0(8位數(shù)據(jù))時(shí),希望傳輸一組數(shù)據(jù)字或半字中的某個(gè)字節(jié),而不管其它字節(jié)。▲XFERCOUNT:傳輸?shù)臄?shù)據(jù)總數(shù),寄存器中填入(總數(shù)-1)。該域有10位,即最大傳輸數(shù)據(jù)數(shù)目為1024。
傳輸?shù)淖止?jié)總數(shù)為:(XFERCOUNT + 1) *WIDTH。
注意1:在DMA傳輸過(guò)程中,DMA控制器會(huì)遞減該數(shù)值,因此不能在傳輸過(guò)程中或傳輸結(jié)束后,讀出該域而得知預(yù)設(shè)的傳輸數(shù)目。
注意2:如果設(shè)置了TRIGBURST =1,則XFERCOUNT必須是BURSTPOWER的倍數(shù)。
1.5.3DMA通道控制和狀態(tài)寄存器(CTLSTAT)這個(gè)寄存器只有兩個(gè)標(biāo)志位,用戶可以檢查這些標(biāo)志位,獲知當(dāng)前DMA控制器的部分運(yùn)行狀態(tài)。▲VALIDPENDING:延遲的有效位。見(jiàn)0的說(shuō)明。▲TRIG:觸發(fā)標(biāo)志。該位表示是否有觸發(fā)條件。設(shè)置觸發(fā)條件有多種途徑:■通道傳輸配置寄存器(XFERCFG)的SWTRIG控制位。■設(shè)置觸發(fā)控制寄存器(SETTRIG0) ,該寄存器可以同時(shí)設(shè)置一個(gè)或多個(gè)通道的觸發(fā)條件。■DMA觸發(fā)輸入模塊選定的硬件觸發(fā)信號(hào),滿足TRIGPOL和TRIGTYPE時(shí)。清除觸發(fā)條件也有多種途徑:■當(dāng)CLRTRIG=1時(shí),描述符指定的傳輸結(jié)束時(shí),清除觸發(fā)條件。
■當(dāng)失能DMA控制器時(shí),見(jiàn)CTRL寄存器。
1.6描述符有效位的延遲設(shè)置機(jī)制通道傳輸配置寄存器(XFERCFG)的CFGVALID位,指定該描述符是否有效。當(dāng)一個(gè)有效的描述符被讀入DMA控制器后,當(dāng)CTLSTAT寄存器的TRIG標(biāo)志被設(shè)置后,DMA傳輸就會(huì)立即開(kāi)始,這是最理想的情況。通常的情況是,當(dāng)準(zhǔn)備好一個(gè)描述符A,尤其是使用描述符鏈時(shí),描述符A所對(duì)應(yīng)的存儲(chǔ)區(qū)的數(shù)據(jù)可能還沒(méi)有準(zhǔn)備好,循環(huán)的乒乓結(jié)構(gòu)就是一個(gè)很好的例子。這種情況下,就需要先設(shè)置描述符A的CFGVALID=0,待數(shù)據(jù)區(qū)準(zhǔn)備好后再設(shè)置它為有效。這樣延遲設(shè)置描述符有效,是通過(guò)SETVALID0寄存器來(lái)完成。
SETVALID0寄存器的每一位對(duì)應(yīng)一個(gè)DMA通道,第n位寫’1’表示延遲設(shè)置通道n的描述符有效。
使用SETVALID0寄存器實(shí)現(xiàn)延遲設(shè)置描述符有效,是為了避免設(shè)置錯(cuò)誤。設(shè)想一下,當(dāng)DMA控制器已經(jīng)在運(yùn)行鏈上的某個(gè)描述符B時(shí),軟件無(wú)法知道另一個(gè)描述符A是否已經(jīng)被讀入DMA控制器。如果它還未被讀入DMA控制器,則可以直接操作描述符A所在的存儲(chǔ)區(qū);如果它已經(jīng)被讀入DMA控制器,則應(yīng)該操作XFERCFG寄存器。
SETVALID0寄存器就是為了正確地設(shè)置描述符的有效位。
經(jīng)過(guò)以上介紹可以看到,如果當(dāng)前加載到DMA控制器的描述符是有效的,設(shè)置SETVALID0寄存器表示延遲設(shè)置鏈中下一個(gè)描述符為有效,此時(shí)CTLSTAT寄存器的VALIDPENDING為‘1’,標(biāo)示這種狀態(tài);當(dāng)下一個(gè)描述符被讀入DMA控制器時(shí),經(jīng)延遲的設(shè)置描述符有效的操作才最終完成,此時(shí)VALIDPENDING位被清除。如果當(dāng)前加載到DMA控制器的描述符是無(wú)效的,設(shè)置SETVALID0寄存器表示改變當(dāng)前這個(gè)描述符為有效,不需經(jīng)過(guò)延遲,操作立即生效。
使用這種延遲機(jī)制,軟件可以從容地先準(zhǔn)備好描述符鏈,然后再按部就班地準(zhǔn)備好數(shù)據(jù)區(qū),逐步推進(jìn)數(shù)據(jù)傳輸進(jìn)程,而不必費(fèi)周折查詢等待DMA控制器的狀態(tài)。
1.7若干DMA傳輸例程
本節(jié)的幾個(gè)例程,分別展示幾種DMA的常見(jiàn)用法。所有例程都會(huì)用到這樣幾個(gè)結(jié)構(gòu)體。
結(jié)構(gòu)體DMA_CHDESC_T是所有通道的描述符鏈中的第一個(gè)描述符。
typedef struct {
|
|
uint32_t notused; // 第一個(gè)描述符的這個(gè)位置是保留位
|
|
uint32_t source; // DMA傳輸源數(shù)據(jù)區(qū)的末地址
|
|
uint32_t dest; // DMA傳輸目標(biāo)數(shù)據(jù)區(qū)的末地址
|
|
uint32_t next; // 鏈接到下一個(gè)描述符
|
|
} DMA_CHDESC_T;
|
ALIGN(512) DMA_CHDESC_T Chan_Desc_Table[];
|
每個(gè)通道的第一個(gè)描述符,必須放在這個(gè)數(shù)組中與通道編號(hào)對(duì)應(yīng)的單元中。例如USART1_RX_DMA通道的第一個(gè)描述符需要放在數(shù)組的第2個(gè)單元中(見(jiàn)表1)。
結(jié)構(gòu)體DMA_RELOADDESC_T適用于其它描述符。所有描述符必須位于16字節(jié)對(duì)齊的內(nèi)存地址。
typedef struct {
|
|
uint32_t xfercfg; // 描述符的傳輸配置寄存器
|
|
uint32_t source; // DMA傳輸源數(shù)據(jù)區(qū)的末地址
|
|
uint32_t dest; // DMA傳輸目標(biāo)數(shù)據(jù)區(qū)的末地址
|
|
uint32_t next; // 鏈接到下一個(gè)描述符
|
|
} DMA_RELOADDESC_T;
|
使用DMA的最簡(jiǎn)單應(yīng)用就是在內(nèi)存中拷貝一個(gè)數(shù)據(jù)塊,這是一個(gè)非常有效率的搬移數(shù)據(jù)塊的方法,尤其是數(shù)據(jù)量比較大時(shí),CPU可以同時(shí)執(zhí)行更多的操作。DMA拷貝數(shù)據(jù)塊不涉及到任何外設(shè)請(qǐng)求,使用軟件觸發(fā),所以也不涉及到任何硬件的觸發(fā)。
本例程先用隨機(jī)數(shù)初始化數(shù)組Buffer1[ ],然后用DMA從Buffer1[ ]傳送數(shù)據(jù)至Buffer2[ ]。數(shù)組定義如下:
下面是初始化DMA控制器,并啟動(dòng)DMA的函數(shù)。代碼片段1.使用DMA在內(nèi)存中拷貝一個(gè)數(shù)據(jù)塊
01 void DMA_M2M_Init(uint32_t *buf1, uint32_t *buf2, uint32_t length)
02 { uint32_t ch_cfg_val, xfercount, xfercfg;
03
04 LPC_SYSCON->SYSAHBCLKCTRL |= DMA;
05 LPC_DMA->CTRL = 0;
06
07 LPC_DMA->SRAMBASE = (uint32_t)(&Chan_Desc_Table);
08
09 xfercount = length - 1;
10 ch_cfg_val = 0;
11 xfercfg = 0 << DMA_XFERCFG_CFGVALID | // 暫時(shí)設(shè)置為無(wú)效
12 0 << DMA_XFERCFG_RELOAD | // 沒(méi)有下一個(gè)描述符
13 1 << DMA_XFERCFG_SWTRIG | // 軟件觸發(fā)
14 1 << DMA_XFERCFG_CLRTRIG | // 傳輸結(jié)束時(shí)清除觸發(fā)標(biāo)志
15 1 << DMA_XFERCFG_SETINTA | // 傳輸結(jié)束時(shí)設(shè)置INTA中斷
16 0 << DMA_XFERCFG_SETINTB |
17 2 << DMA_XFERCFG_WIDTH | // 數(shù)據(jù)寬度為32位
18 1 << DMA_XFERCFG_SRCINC | // 每次傳輸后源地址遞增
19 1 << DMA_XFERCFG_DSTINC | // 每次傳輸后目標(biāo)地址遞增
20 xfercount << DMA_XFERCFG_XFERCOUNT; // 傳輸長(zhǎng)度
21 LPC_DMA->CHANNEL[CH_USART0_RX].CFG = ch_cfg_val;
22 LPC_DMA->CHANNEL[CH_USART0_RX].XFERCFG = xfercfg;
23
24 Chan_Desc_Table[CH_USART0_RX].source = (uint32_t)(&buf1[xfercount]);
25 Chan_Desc_Table[CH_USART0_RX].dest = (uint32_t)(&buf2[xfercount]);
26 Chan_Desc_Table[CH_USART0_RX].next = (uint32_t)0L;
27
28 LPC_DMA->INTENSET0 = 1 << CH_USART0_RX;
29 LPC_DMA->ENABLESET0 = 1 << CH_USART0_RX;
30
31 LPC_DMA->CTRL = 1;
32
33 LPC_DMA->SETVALID0 = 1 << CH_USART0_RX;
34 // LPC_DMA->SETTRIG0 = 1 << CH_USART0_RX;
35 }
在這個(gè)例程中,用到了宏定義CH_USART0_RX,這是對(duì)應(yīng)USART0接收方向的DMA通道號(hào)(見(jiàn)表1),在頭文件中定義了所有的通道號(hào):
#define CH_USART0_RX 0 // USART0接收就緒
|
|
#define CH_USART0_TX 1 // USART0發(fā)送就緒
|
|
#define CH_USART1_RX 2 // USART1接收就緒
|
|
#define CH_USART1_TX 3 // USART1發(fā)送就緒
|
|
......
|
|
......
|
ALIGN(512) DMA_CHDESC_T Chan_Desc_Table[1];
|
在本例程中,由于使用的是通道0,而沒(méi)有使用其它通道,所以在數(shù)組中只配置了一個(gè)單元。
原則上,這個(gè)數(shù)組中對(duì)應(yīng)不使用的通道的描述符單元,可以挪做其它用途。
在這個(gè)例程里,配置好所有的寄存器和描述符,并且使能了整個(gè)DMA控制器后,在第33行配置了對(duì)應(yīng)的傳輸符為有效,該語(yǔ)句執(zhí)行后DMA傳輸立即開(kāi)始了。
如果第13行沒(méi)有配置XFERCFG的“軟件觸發(fā)”位,執(zhí)行第33行后DMA通道還需要等待觸發(fā)信號(hào)才能開(kāi)始傳輸,第34行就是由軟件發(fā)出DMA觸發(fā)信號(hào)的另一種途徑。
由上面的介紹可以看出,可以有多種方式,靈活地安排啟動(dòng)DMA傳輸?shù)臅r(shí)機(jī)和方法,讓用戶可以更加有效地安排自己的應(yīng)用流程。
下面是這個(gè)例程的主函數(shù)和中斷處理程序。
代碼片段2.使用DMA拷貝一個(gè)數(shù)據(jù)塊的中斷函數(shù)和主函數(shù)
01 uint8_t DMA_IntA_Flag;
02 void DMA_IRQHandler(void) // DMA中斷處理程序
03 {
04 if (LPC_DMA->INTA0 & (1 << CH_USART0_RX)) {
05 LPC_DMA->INTA0 = 1 << CH_USART0_RX;
06 DMA_IntA_Flag = 1;
07 }
08 if (LPC_DMA->ERRINT0 & (1 << CH_USART0_RX))
09 LPC_DMA->ERRINT0 = 1 << CH_USART0_RX;
10 }
11
12 void main()
13 { uint32_t pp;
14 for (pp = 0; pp < BUF_SIZE; pp++)
15 Buffer1[pp] = rand();
16
17 DMA_IntA_Flag = 0;
18 DMA_M2M_Init(Buffer1, Buffer2, BUF_SIZE);
19
20 NVIC_EnableIRQ(DMA_IRQn);
21 do {
22 __WFI();
23 } while (DMA_IntA_Flag == 0);
24
25 while (1);
26 }
DMA傳輸結(jié)束后產(chǎn)生中斷,在中斷函數(shù)中將軟件標(biāo)志置’1’,主函數(shù)可以知道DMA傳輸是否已經(jīng)完成。在實(shí)際的項(xiàng)目中,用戶程序可以替換上述21~23行的代碼,執(zhí)行其它的一些操作。
1.7.2DMA執(zhí)行USART0的連續(xù)發(fā)送(硬件觸發(fā))下面這個(gè)例程是使用DMA通過(guò)USART0發(fā)送一個(gè)字符串,需要配置使用USART0的發(fā)送請(qǐng)求,并采用開(kāi)發(fā)板上的USER_KEY產(chǎn)生硬件觸發(fā),即按下按鍵后才送出所有數(shù)據(jù),用戶可以在PC端的虛擬串口上看到送出的字符串。
首先還是DMA的初始化函數(shù),這個(gè)函數(shù)與前面的數(shù)據(jù)塊搬運(yùn)初始化基本一致,指示CFG和XFERCFG寄存器的內(nèi)容有所變化。
代碼片段3. DMA通過(guò)USART0發(fā)送字符串01 void DMA_UART_Send(uint8_t *buf, uint32_t length)
02 { uint32_t ch_cfg_val, xfercount, xfercfg;
03
04 LPC_SYSCON->SYSAHBCLKCTRL |= DMA;
05 LPC_DMA->CTRL = 0;
06
07 LPC_DMA->SRAMBASE = (uint32_t)(&Chan_Desc_Table);
08
09 xfercount = length - 1;
10 ch_cfg_val = 1 << DMA_CFG_PERIPHREQEN | // 外設(shè)請(qǐng)求
11 1 << DMA_CFG_HWTRIGEN | // 硬件觸發(fā)
12 0 << DMA_CFG_TRIGTYPE | // 邊沿觸發(fā)
13 0 << DMA_CFG_TRIGPOL; // 下降沿觸發(fā)
14
15 xfercfg = 0 << DMA_XFERCFG_CFGVALID | // 暫時(shí)設(shè)置為無(wú)效
16 0 << DMA_XFERCFG_RELOAD | // 沒(méi)有下一個(gè)描述符
17 0 << DMA_XFERCFG_SWTRIG | // 沒(méi)有軟件觸發(fā)
18 1 << DMA_XFERCFG_CLRTRIG | // 傳輸結(jié)束時(shí)清除觸發(fā)標(biāo)志
19 1 << DMA_XFERCFG_SETINTA | // 傳輸結(jié)束時(shí)設(shè)置INTA中斷
20 0 << DMA_XFERCFG_SETINTB |
21 0 << DMA_XFERCFG_WIDTH | // 數(shù)據(jù)寬度為8位
22 1 << DMA_XFERCFG_SRCINC | // 每次傳輸后源地址遞增
23 0 << DMA_XFERCFG_DSTINC | // 每次傳輸后目標(biāo)地址不遞增
24 xfercount << DMA_XFERCFG_XFERCOUNT; // 傳輸長(zhǎng)度
25 LPC_DMA->CHANNEL[CH_USART0_TX].CFG = ch_cfg_val;
26 LPC_DMA->CHANNEL[CH_USART0_TX].XFERCFG = xfercfg;
27
28 Chan_Desc_Table[CH_USART0_TX].source = (uint32_t)(&buf[xfercount]);
29 Chan_Desc_Table[CH_USART0_TX].dest = (uint32_t)(&LPC_USART0->TXDAT);
30 Chan_Desc_Table[CH_USART0_TX].next = (uint32_t)0L;
31
32 LPC_DMA->INTENSET0 = 1 << CH_USART0_TX;
33 LPC_DMA->ENABLESET0 = 1 << CH_USART0_TX;
34
35 LPC_DMA->CTRL = 1;
36
37 LPC_DMA->SETVALID0 = 1 << CH_USART0_TX;
38 }
上述代碼里用橙色標(biāo)注出與代碼片段1不同的地方。還有一個(gè)明顯的不同是,此處所有涉及到DMA通道號(hào)時(shí),都換成了CH_USART0_TX。
USART0的初始化部分與USART章節(jié)的代碼完全一致,現(xiàn)抄錄如下。
代碼片段4.基本UART收發(fā)例程的USART0初始化
00 void USART0_init() {
01 LPC_SYSCON->SYSAHBCLKCTRL |= (UART0 | SWM);
02
03 LPC_SYSCON->PRESETCTRL &= (UART0_RST_N);
04 LPC_SYSCON->PRESETCTRL |= ~(UART0_RST_N);
05
06 ConfigSWM(U0_TXD, P0_4);
07 ConfigSWM(U0_RXD, P0_0);
08
09 LPC_SYSCON->UARTCLKDIV = LPC_SYSCON->SYSAHBCLKDIV; // 設(shè)置USART時(shí)鐘的分頻系數(shù)
10 LPC_SYSCON->UARTFRGMULT = 4;
11 LPC_SYSCON->UARTFRGDIV = 255;
12 LPC_USART0->BRG = 16 - 1;
13
14 // 8個(gè)數(shù)據(jù)位,無(wú)校驗(yàn)位,1個(gè)停止位,沒(méi)有硬件流控,異步模式
15 LPC_USART0->CFG = DATA_LENG_8 | PARITY_NONE | STOP_BIT_1;
16
17 LPC_USART0->CTL = 0;
18
19 LPC_USART0->STAT = 0xFFFF;
20
21 LPC_USART0->INTENSET = RXRDY;
22 NVIC_EnableIRQ(UART0_IRQn);
23 }
接下來(lái)是本節(jié)的重點(diǎn)。代碼片段3的第11~13行,配置DMA通道為硬件觸發(fā)信號(hào)的下降沿觸發(fā),下面是初始化PINTINT,使用USER_KEY產(chǎn)生觸發(fā)信號(hào),和對(duì)應(yīng)的中斷程序。代碼片段5.初始化按鍵產(chǎn)生DMA硬件觸發(fā)信號(hào)
01 #define PINTSEL0 0 // 定義引腳中斷0的編號(hào)
02 #define KEY_USER P0_1 // 定義按鍵USER_KEY的引腳
03
04 void PININT0_IRQHandler(void)
05 {
06 if (LPC_PIN_INT->RISE & (1<
07 LPC_PIN_INT->RISE = 1<// 清除上升沿中斷標(biāo)志
08 if (LPC_PIN_INT->FALL & (1<
09 LPC_PIN_INT->FALL = 1<// 清除下降沿中斷標(biāo)志
10 }
11
12 void PINT_Init_Key_User()
13 {
14 LPC_GPIO_PORT->DIRCLR0 = 1 << KEY_USER; // 配置USER_KEY對(duì)應(yīng)的引腳為輸入
15 LPC_SYSCON->PINTSEL[PINTSEL0] = KEY_USER; // USER_KEY對(duì)應(yīng)對(duì)應(yīng)到引腳中斷0(PINTSEL0)
16 LPC_PIN_INT->ISEL = 0 << PINTSEL0; // 配置引腳中斷0(PINTSEL0)為邊沿觸發(fā)
17 LPC_PIN_INT->IENR = 1 << PINTSEL0; // 配置引腳中斷0(PINTSEL0)是上升沿觸發(fā)
18 LPC_PIN_INT->IENF = 0 << PINTSEL0; // 配置引腳中斷0(PINTSEL0)不是下降沿觸發(fā)
19 LPC_PIN_INT->IST = 0xFF; // 清除所有可能的引腳中斷標(biāo)志
20 NVIC_EnableIRQ(PININT0_IRQn); // 使能引腳中斷
21 }
上述代碼是對(duì)PINTINT的初始化,它配置USER_KEY對(duì)應(yīng)的引腳產(chǎn)生一個(gè)中斷信號(hào),確切地說(shuō)是按鍵按下再抬起時(shí)的上升沿將產(chǎn)生中斷。第04行的中斷處理程序中,只是簡(jiǎn)單地清除可能的上升沿或下降沿中斷標(biāo)志。
這里要澄清兩個(gè)概念,一個(gè)是引腳中斷的觸發(fā)信號(hào),另一個(gè)是DMA的觸發(fā)信號(hào)。前者是引腳上的信號(hào),用于產(chǎn)生中斷;后者是芯片內(nèi)部的中斷標(biāo)志對(duì)應(yīng)的信號(hào),用于觸發(fā)DMA傳輸。兩個(gè)信號(hào)分別有上升沿和下降沿的選項(xiàng),但兩者是不等價(jià)的,本例程中引腳中斷選擇的是上升沿,而DMA觸發(fā)信號(hào)選擇的是下降沿。
下圖顯示出了這兩個(gè)信號(hào)之間的關(guān)系:
? ? ? ? ? ? ? ? ? ? ??圖5.引腳中斷與DMA觸發(fā)信號(hào)的關(guān)系圖
圖中的時(shí)間點(diǎn)④是觸發(fā)DMA傳輸?shù)臅r(shí)間,這個(gè)時(shí)間點(diǎn)與代碼片段5第07行的執(zhí)行相對(duì)應(yīng)。如果清除上升沿中斷的動(dòng)作被推遲,則DMA傳輸?shù)臅r(shí)間也會(huì)被推遲,所以用戶要盡快地響應(yīng)PINTINT中斷并清除中斷標(biāo)志,以實(shí)現(xiàn)快速高效傳輸。
下面是主函數(shù)代碼,主函數(shù)中調(diào)用了前面介紹過(guò)的DMA、USART和PINTINT函數(shù)。
代碼片段6.硬件觸發(fā)DMA傳輸UART發(fā)送數(shù)據(jù)例程
01 const unsigned char Hello[] = "Hello DMA World!
"; // 待發(fā)送的數(shù)據(jù)串
02 void main()
03 {
04 USART0_init(); // 初始化USART0
05
06 DMA_IntA_Flag = 0; // 清除DMA中斷標(biāo)記
07
08 DMA_UART_Send((uint8_t *)Hello, sizeof(Hello)-1); // 初始化DMA控制器
09
10 LPC_DMATRIGMUX->DMA_ITRIG_INMUX1 = 0x05;
11 PINT_Init_Key_User();
12
13 NVIC_EnableIRQ(DMA_IRQn);
14 do {
15 __WFI();
16 }
17 while (DMA_IntA_Flag == 0);
18
19 while (1);
20 }
這個(gè)主函數(shù)與前面那個(gè)例程(見(jiàn)代碼片段2)的主要區(qū)別,就是第10、11行配置DMA觸發(fā)源和對(duì)觸發(fā)源(引腳中斷0)的初始化。
這里需要注意的是第10行配置DMA觸發(fā)源,一定要在使能DMA時(shí)鐘之后執(zhí)行。本例程中,DMA的時(shí)鐘是在DMA_UART_Send()中設(shè)置的,見(jiàn)代碼片段3。
1.7.3DMA執(zhí)行USART0的成組發(fā)送
這個(gè)例程是在上一個(gè)例程的基礎(chǔ)上,增加了使用乒乓鏈接的描述符,同時(shí)設(shè)置成組傳輸。
下圖所示為本例程中乒乓鏈接的描述符鏈。鏈頭描述符指向一個(gè)開(kāi)始字符串Hello,并鏈接到描述符B。描述符A指向一個(gè)字符串A,描述符B指向另一個(gè)字符串B。傳輸執(zhí)行的效果是,先發(fā)送Hello,然后循環(huán)發(fā)送SpingBàSpingAàSpingB......。
圖6.DMA執(zhí)行USART0的成組發(fā)送例程的描述符鏈另外,例程中安排了以成組(Burst)方式發(fā)送每個(gè)字符串,即每次觸發(fā)只發(fā)送BURSTPOWER指定長(zhǎng)度的數(shù)據(jù),見(jiàn)1.5.1的說(shuō)明。
和上節(jié)一樣,例程中的觸發(fā)源也是與USER_KEY相連的引腳中斷0。
本例程用到下述變量。
ALIGN(512) DMA_CHDESC_T Chan_Desc_Table[2]; // 所有通道描述符鏈頭數(shù)組
ALIGN(16) DMA_RELOADDESC_T Descriptor_A; // 描述符A
ALIGN(16) DMA_RELOADDESC_T Descriptor_B; // 描述符B
uint8_t DMA_IntA_Flag; // 中斷A軟件標(biāo)志
uint8_t DMA_IntB_Flag; // 中斷B軟件標(biāo)志
// 1234----1234----1234----1234----
const uint8_t Hello[] = ">> Hello my DMA world!
"; // 鏈頭對(duì)應(yīng)的字符串
const uint8_t StringA[] = ">> Hello dear StringA.
"; // 描述符A的字符串
const uint8_t StringB[] = "<< Hello I am saying String B.
"; // 描述符B的字符串
下面是DMA初始化的代碼代碼片段7.執(zhí)行USART0的成組發(fā)送例程的DMA初始化代碼
01 void DMA_UART_PingPong_Send(uint8_t *buf, uint32_t length)
02 { uint32_t ch_cfg_val, xfercount, xfercfg;
03
04 LPC_SYSCON->SYSAHBCLKCTRL |= DMA;
05 LPC_DMA->CTRL = 0;
06
07 LPC_DMA->SRAMBASE = (uint32_t)(&Chan_Desc_Table);
08
09 xfercount = length - 1;
10 ch_cfg_val = 1 << DMA_CFG_PERIPHREQEN | // 外設(shè)請(qǐng)求
11 1 << DMA_CFG_HWTRIGEN | // 硬件觸發(fā)
12 0 << DMA_CFG_TRIGTYPE | // 邊沿觸發(fā)
13 0 << DMA_CFG_TRIGPOL | // 下降沿觸發(fā)
14 1 << DMA_CFG_TRIGBURST | // 成組傳輸模式
15 3 << DMA_CFG_BURSTPOWER; // 每組為8(23)個(gè)數(shù)據(jù)
16
17 xfercfg = 0 << DMA_XFERCFG_CFGVALID | // 暫時(shí)設(shè)置為無(wú)效
18 1 << DMA_XFERCFG_RELOAD | // 有下一個(gè)描述符
19 0 << DMA_XFERCFG_SWTRIG | // 沒(méi)有軟件觸發(fā)
20 1 << DMA_XFERCFG_CLRTRIG | // 傳輸結(jié)束時(shí)清除觸發(fā)標(biāo)志
21 1 << DMA_XFERCFG_SETINTA | // 傳輸結(jié)束時(shí)設(shè)置INTA中斷
22 0 << DMA_XFERCFG_SETINTB |
23 0 << DMA_XFERCFG_WIDTH | // 數(shù)據(jù)寬度為8位
24 1 << DMA_XFERCFG_SRCINC | // 每次傳輸后源地址遞增
25 0 << DMA_XFERCFG_DSTINC | // 每次傳輸后目標(biāo)地址不遞增
26 xfercount << DMA_XFERCFG_XFERCOUNT; // 傳輸長(zhǎng)度
27 LPC_DMA->CHANNEL[CH_USART0_TX].CFG = ch_cfg_val;
28 LPC_DMA->CHANNEL[CH_USART0_TX].XFERCFG = xfercfg;
29
30 Chan_Desc_Table[CH_USART0_TX].source = (uint32_t)(&buf[xfercount]);
31 Chan_Desc_Table[CH_USART0_TX].dest = (uint32_t)(&LPC_USART0->TXDAT);
32 Chan_Desc_Table[CH_USART0_TX].next = (uint32_t)& Descriptor_B;
33
34 xfercount = sizeof(StringB)- 2; // 去掉字符串結(jié)尾的字符’’
35 Descriptor_B.xfercfg = 1 << DMA_XFERCFG_CFGVALID |
36 1 << DMA_XFERCFG_RELOAD |
37 1 << DMA_XFERCFG_CLRTRIG |
38 1 << DMA_XFERCFG_SETINTB |
39 1 << DMA_XFERCFG_SRCINC |
40 xfercount << DMA_XFERCFG_XFERCOUNT;
41 Descriptor_B.source = (uint32_t)(&StringB[xfercount]);
42 Descriptor_B.dest = (uint32_t)(&LPC_USART0->TXDAT);
43 Descriptor_B.next = (uint32_t)&Descriptor_A;
44
45 xfercount = sizeof(StringA)- 2; // 去掉字符串結(jié)尾的字符’’
46 Descriptor_A.xfercfg = 1 << DMA_XFERCFG_CFGVALID |
47 1 << DMA_XFERCFG_RELOAD |
48 1 << DMA_XFERCFG_CLRTRIG |
49 1 << DMA_XFERCFG_SETINTA |
50 1 << DMA_XFERCFG_SRCINC |
51 xfercount << DMA_XFERCFG_XFERCOUNT;
52 Descriptor_A.source = (uint32_t)(&StringA[xfercount]);
53 Descriptor_A.dest = (uint32_t)(&LPC_USART0->TXDAT);
54 Descriptor_A.next = (uint32_t)&Descriptor_B;
55
56 LPC_DMA->INTENSET0 = 1 << CH_USART0_TX;
57 LPC_DMA->ENABLESET0 = 1 << CH_USART0_TX;
58
59 LPC_DMA->CTRL = 1;
60
61 LPC_DMA->SETVALID0 = 1 << CH_USART0_TX;
62 }
DMA控制器中設(shè)置的兩個(gè)中斷INTA和INTB的內(nèi)部機(jī)制完全一致,分為兩個(gè)中斷源只是為了讓用戶區(qū)分對(duì)應(yīng)的描述符,用戶可以自由安排。本例中設(shè)置描述符A執(zhí)行結(jié)束后會(huì)產(chǎn)生中斷A,描述符B執(zhí)行結(jié)束后會(huì)產(chǎn)生中斷B,因此相比前一個(gè)例程中斷處理程序也多了出來(lái)INTB的代碼。
代碼片段8.執(zhí)行USART0的成組發(fā)送例程中斷處理程序
01 void DMA_IRQHandler(void)
02 {
03 if (LPC_DMA->INTA0 & (1 << CH_USART0_TX)) {
04 LPC_DMA->INTA0 = 1 << CH_USART0_TX;
05 DMA_IntA_Flag++;
06 }
07 if (LPC_DMA->INTB0 & (1 << CH_USART0_TX)) {
08 LPC_DMA->INTB0 = 1 << CH_USART0_TX;
09 DMA_IntB_Flag++;
10 }
11 if (LPC_DMA->ERRINT0 & (1 << CH_USART0_TX))
12 LPC_DMA->ERRINT0 = 1 << CH_USART0_TX;
13 }
每次執(zhí)行完一個(gè)描述符后,就會(huì)相應(yīng)地產(chǎn)生一個(gè)中斷(INTA或INTB),用戶可以自行在代碼片段8的第05和09行安插代碼在適當(dāng)?shù)臅r(shí)候結(jié)束整個(gè)描述符鏈的DMA傳輸,例如當(dāng)循環(huán)次數(shù)滿足一定要求時(shí)。
代碼片段9.執(zhí)行USART0的成組發(fā)送例程的主函數(shù)
01 void main()
02 {
03 USART0_init(); // 初始化USART0
04
05 DMA_IntA_Flag = DMA_IntB_Flag = 0; // 清除DMA中斷標(biāo)記
06
07 DMA_UART_PingPong_Send((uint8_t *)Hello, sizeof(Hello)-1); // 初始化DMA控制器
08
09 LPC_DMATRIGMUX->DMA_ITRIG_INMUX1 = 0x05;
10 PINT_Init_Key_User();
11
12 NVIC_EnableIRQ(DMA_IRQn);
13 while (1)
14 __WFI();
15 }
主函數(shù)和前面的代碼片段6基本一致。執(zhí)行這個(gè)例程后,每按一次按鍵DMA會(huì)發(fā)送8個(gè)字符,在串口助手上有如下顯示:
END
更多恩智浦AI-IoT市場(chǎng)和產(chǎn)品信息,邀您同時(shí)關(guān)注“NXP客?!蔽⑿殴娞?hào)
? ? ?NXP客棧
恩智浦致力于打造安全的連接和基礎(chǔ)設(shè)施解決方案,為智慧生活保駕護(hù)航。
長(zhǎng)按二維碼,關(guān)注我們
恩智浦MCU加油站
這是由恩智浦官方運(yùn)營(yíng)的公眾號(hào),著重為您推薦恩智浦MCU的產(chǎn)品信息、開(kāi)發(fā)技巧、教程文檔、培訓(xùn)課程等內(nèi)容。
長(zhǎng)按二維碼,關(guān)注我們
原文標(biāo)題:LPC800前生今世-第九章 直接存儲(chǔ)器訪問(wèn) (DMA)
文章出處:【微信公眾號(hào):恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
mcu
+關(guān)注
關(guān)注
146文章
16667瀏覽量
347769 -
恩智浦
+關(guān)注
關(guān)注
14文章
5788瀏覽量
104682
原文標(biāo)題:LPC800前生今世-第九章 直接存儲(chǔ)器訪問(wèn) (DMA)
文章出處:【微信號(hào):NXP_SMART_HARDWARE,微信公眾號(hào):恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論