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