直接存儲器訪問(DMA)控制器執行數據傳輸任務,并從ADuCM4050微控制器單元(MCU)卸載這些任務。DMA 在外設和存儲器之間提供高速數據傳輸。DMA 無需任何 CPU 操作即可快速移動數據,從而使 CPU 資源可用于其他操作。
本應用筆記介紹如何使用ADuCM4050微控制器的DMA功能。本應用筆記還提供了對DMA控制器進行編程所需的編程步驟、DMA圖和DMA代碼片段。
DMA 控制器特性
DMA 控制器支持以下功能:
27 個獨立的 DMA 通道
每個 DMA 通道的兩個可編程優先級
每個優先級都使用由 DMA 通道號確定的固定優先級進行仲裁
每個 DMA 通道都可以訪問主通道和/或備用通道控制數據結構
支持以下傳輸類型:
內存到內存
內存到外設
內存外設
支持以下 DMA 循環類型:
基本
自動請求
乒乓球
分散聚集
支持多種 DMA 傳輸數據寬度(8 位、16 位和 32 位)
每個 DMA 通道都可以具有獨立的源和目標遞增和遞減控制
DMA
DMA 通道
DMA 有 27 個通道,每個通道專用于管理來自外設的內存訪問請求。
表 1 顯示了 DMA 通道分配。
通道節點 | 外設 |
0 | SPI2 傳輸 |
1 | SPI2 接收 |
2 | 運動0A |
3 | 運動0B |
4 | SPI0 傳輸 |
5 | SPI0 接收 |
6 | SPI1 傳輸 |
7 | SPI1 接收 |
8 | UART0 傳輸 |
9 | UART0 接收 |
10 | 我2C 從機傳輸 |
11 | 我2C 從接收 |
12 | 我2C 主控 |
13 | 密碼學 |
14 | 密碼學輸出 |
15 | 閃光 |
16 到 23 | 軟件 DMA |
24 | 模數轉換器 (ADC) 接收 |
25 | UART1 傳輸 |
26 | UART1 接收 |
DMA系統框圖
圖 1 顯示了 DMA 系統框圖。
圖1.DMA 系統框圖
DMA 如何工作?
DMA 根據 DMA 控制器中提供的信息在內存和外設之間傳輸數據。當生成 DMA 請求(通過軟件 DMA 請求或外設 DMA 請求)時,DMA 控制器從相應的通道控制數據結構中收集信息并執行所需的傳輸。
DMA 控制器
DMA 控制器是執行傳輸的 DMA 塊的主要部分??刂破鲝耐ǖ揽刂茢祿Y構中獲取傳輸信息,并在收到DMA傳輸請求后執行事務。
通道控制數據結構
每個通道都有兩個控制數據結構:主通道和備用通道。必須聲明一個與存儲器分開的空間,其中包含每個通道的主數據和備用數據結構,具體取決于應用復雜性。此內存空間是 DMA 控制器獲取有關傳輸的信息的位置。
主數據結構和備用數據結構以及數據庫指針
主數據結構和備用數據結構包含所有 DMA 通道的 DMA 描述符。這些數據結構在 RAM 區域中聲明,每個通道描述符有 16 個字節。數據庫指針包含這些描述符的起始地址,這是 DMA 控制器了解特定通道的描述符信息拾取位置的方式。收到傳輸請求時,DMA 控制器讀取基指針,遍歷到通道的指定描述符,并按照描述符指定的方式執行傳輸。如果描述符信息在另一個內存空間中定義,請在啟用 DMA 控制器之前將描述符信息移動到通道數據結構內存空間。備用數據結構基指針是一個只讀寄存器,當主描述符基指針更改時,其值會自動調整。
16 字節 DMA 描述符由所有 DMA 事務信息組成。在啟用 DMA 控制器之前,必須將描述符復制到特定的通道控制數據結構,因為 DMA 從通道控制數據結構中獲取有關傳輸的信息。表 2 顯示了 DMA 描述符通道的元素。
名字 | 描述 | 大小(字節) |
SRC_END_PTR | 源數據的結束地址 | 4 |
DST_END_PTR | 目標數據的結束地址 | 4 |
CHL_CFG | 提供 DMA 傳輸的控制信息 | 4 |
保留 | 保留 | 4 |
DMA 編程模型
DMA 設置的一般編程順序如下:
設置用于數據傳輸的 DMA 描述符。
設置 DMA 基指針。
啟用所需的 DMA 通道。
在 DMA 配置寄存器 (DMA_CFG) 中啟用 DMA 控制器。
在 DMA 通道軟件請求寄存器 (DMA_SWREQ) 中生成軟件 DMA 請求,或啟用生成 DMA 控制器中斷的外設。
有關每種模式的 DMA 設置的特定信息,請參閱 DMA 模式部分。
數字對象模式
自動請求 (ARQ) 模式和基本模式
ARQ 和基本模式是所有模式中最基本的,由單個描述符傳輸組成。ARQ 模式用于內存到內存的傳輸,基本模式用于內存到外設或外設到內存的傳輸。
在這兩種模式下,DMA描述符可以先構建,然后移動到通道數據結構中,也可以直接寫入通道數據結構。
這些模式的編程順序如下:
填充 DMA 描述符并將其移動到通道數據結構或直接寫入通道數據結構。
將主控制數據庫 (PCD) 指針指向通道控制數據結構的基。
清除特定 DMA 通道的通道請求掩碼。
啟用 DMA 的請求源中斷和 DMA 完成中斷(可選)。
啟用相應的 DMA 通道。
通過將DMA_CFG寄存器設置為 1 來啟用 DMA 控制器。
生成軟件 DMA 請求 (ARQ DMA) 或等待生成外圍 DMA 請求(基本 DMA)。
ARQ 和基本模式一次最多允許 1024 次傳輸,并且每次傳輸后必須重新啟用 DMA 控制器。
基本模式的時序圖如圖 2 所示,除步驟 7 外,與 ARQ 模式相同。
圖2.基本模式序列圖
示例代碼:RAM 的兩個數據塊內的內存到內存 ARQ 傳輸
此示例演示如何在內存塊之間執行 DMA ARQ 傳輸。此示例使用通道 16 DMA(軟件 DMA)描述符,并將數據從 srcPtr[] 復制到 destPtr[]。
/* Build the channel descriptor */
ChannelDesc[16].srcEndPtr =(unsigned int) &srcPtr[61];
ChannelDesc[16].destEndPtr =(unsigned int) &destPtr[61];
ChannelDesc[16].ctrlCfg.src_inc = 0;
ChannelDesc[16].ctrlCfg.dst_inc = 0;
ChannelDesc[16].ctrlCfg.src_size = 0;
ChannelDesc[16].ctrlCfg.n_minus_1 = 61u;
ChannelDesc[16].ctrlCfg.r_power = 0;
ChannelDesc[16].ctrlCfg.cycle_ctrl = 2u;
/* give the address of the built channel descriptor to the DMA controller */
*pREG_DMA0_PDBPTR = (unsigned int) &ChannelDesc[0];
/* Enable the DMA channel 16 */
*pREG_DMA0_EN_SET = (1u << 16u);
/* Enable the peripherals to create DMA requests on channel 16 */
*pREG_DMA0_RMSK_CLR = (1u << 16u);
/* enable the DMA controller */
*pREG_DMA0_CFG = 1u;
/* Generate a software DMA request on channel 16 */
*pREG_DMA0_SWREQ = (1u << 16u);
示例代碼:使用 UART 進行內存到外設基本模式數據傳輸
此示例演示如何在內存塊和外設緩沖區寄存器之間執行基本硬件請求傳輸。該示例使用通道 8 DMA(UART0 發送),并將數據從 srcPts[] 發送到通用異步接收器發射器 (UART) 終端。
/* Build the channel descriptor */
ChannelDesc[8].srcEndPtr =(unsigned int) &srcPtr[61];
ChannelDesc[8].destEndPtr =(unsigned int)pREG_UART0_TX;
ChannelDesc[8].ctrlCfg.src_inc = 0;
ChannelDesc[8].ctrlCfg.dst_inc = 3;
ChannelDesc[8].ctrlCfg.src_size = 0;
ChannelDesc[8].ctrlCfg.n_minus_1 = 61u;
ChannelDesc[8].ctrlCfg.r_power = 0;
ChannelDesc[8].ctrlCfg.cycle_ctrl = 1u;
/* enable the DMA controller */
*pREG_DMA0_CFG = 1u;
/* give the address of the built channel descriptor to the DMA controller */
*pREG_DMA0_PDBPTR = (unsigned int) &ChannelDesc[0];
/* Enable the peripherals to create DMA requests on channel 8 */
*pREG_DMA0_RMSK_CLR = (1u << 8u);
/* Enable the DMA channel 8 */
*pREG_DMA0_EN_SET = (1u << 8u);
/* Configure DMA Channel 8 to use primary data structure */
*pREG_DMA0_ALT_CLR = (1u << 8u);
/* pin mux for UART 0 */
*((volatile uint32_t *)REG_GPIO0_CFG) |= UART0_TX_PORTP0_MUX | UART0_RX_PORTP0_MUX ;
/* UART 0 configuration */
/* baud rate = 9600*/
*pREG_UART0_DIV = 0x1C;
*pREG_UART0_FBR = (0x1<
*pREG_UART0_LCR2 = (0x3<
/* parity , stop */
*pREG_UART0_LCR = (0x3<
/* Enable DMA request from UART0 to DMA controller */
*pREG_UART0_IEN |=(1u << 4u);
內存分散收集和外設分散收集模式
內存分散收集模式是重復 ARQ 模式,外設分散收集模式是重復的基本模式,其中兩種分散收集模式都設置并同時觸發多個 DMA 描述符。當同時執行大型傳輸時,此模式很有用。
此模式涉及設置所有描述符,并將描述符逐個移動到發生 DMA 事務的備用通道數據結構中。描述符從內存到備用通道描述符 (ACD) 的這種移動由主 DMA 執行。因此,特定 DMA 通道的主描述符填充了以下信息:源端是聲明的描述符的結尾,目標是備用通道描述符、字大小和仲裁大小,增量設置為 4。由于仲裁設置為 4,因此 DMA 控制器執行主描述符字大小的四次傳輸(即一個描述符移動到備用描述符),然后轉移到備用描述符,在那里執行實際數據傳輸,然后移回主控制器。循環一直持續到循環達到基本模式 DMA 描述符。DMA 完成中斷在描述符的每次傳輸完成后生成。
散點收集模式的編程順序如下:
使用有關在基本模式下使用最后一個 DMA 描述符執行的傳輸的信息定義 DMA 描述符(如果需要 DMA 事務終止)。
使用將聲明的 DMA 描述符移動到仲裁為 4 的備用通道數據結構的詳細信息填充主通道數據結構。
將 PCD 指針指向通道描述符的基座。
清除特定 DMA 通道的通道請求掩碼。
啟用 DMA 的請求源中斷和 DMA 完成中斷(可選)。
啟用相應的 DMA 通道。
通過將DMA_CFG寄存器設置為 1 來啟用 DMA 控制器。
生成軟件 DMA 請求(內存散射收集 DMA 模式)或等待生成外設 DMA 請求(外設散射收集 DMA 模式)。
散點收集的序列圖如圖 3 所示。
圖3.散點收集模式序列圖
示例代碼:內存到內存分散收集軟件請求 RAM 的兩個數據塊內的數據傳輸
此示例演示如何在內存塊之間執行 DMA 分散收集傳輸。此示例使用 DMA 通道 16(軟件 DMA)描述符,將數據從 srcPtr1[] 復制到 destPtr1[],將 srcPtr2[] 復制到 destPtr2[],將 srcPtr3[] 復制到 destPtr3[]。
/* Build the scatter gather descriptor 1 to copy srcPtr1[] to destPtr1[]*/ ScatterGatherDesc[0].srcEndPtr =(unsigned int) &srcPtr1[9];
ScatterGatherDesc[0].destEndPtr =(unsigned int)&destPtr1[9];
ScatterGatherDesc[0].ctrlCfg.src_inc = 0;
ScatterGatherDesc[0].ctrlCfg.dst_inc = 0;
ScatterGatherDesc[0].ctrlCfg.src_size = 0;
ScatterGatherDesc[0].ctrlCfg.n_minus_1 = 9u;
ScatterGatherDesc[0].ctrlCfg.r_power = 0;
ScatterGatherDesc[0].ctrlCfg.cycle_ctrl = 5u;
/* alternate memory to memory scatter gather */
/* Build the scatter gather descriptor 2 to copy srcPtr2[] to destPtr2[]*/
ScatterGatherDesc[1].srcEndPtr =(unsigned int) &srcPtr2[9];
ScatterGatherDesc[1].destEndPtr =(unsigned int)&destPtr2[9];
ScatterGatherDesc[1].ctrlCfg.src_inc = 0;
ScatterGatherDesc[1].ctrlCfg.dst_inc = 0;
ScatterGatherDesc[1].ctrlCfg.src_size = 0;
ScatterGatherDesc[1].ctrlCfg.n_minus_1 = 9u;
ScatterGatherDesc[1].ctrlCfg.r_power = 0;
ScatterGatherDesc[1].ctrlCfg.cycle_ctrl = 5u;
/* alternate memory to memory scatter gather */
/* Build the scatter gather descriptor 3 to copy srcPtr3[] to destPtr3[]*/
ScatterGatherDesc[2].srcEndPtr =(unsigned int) &srcPtr3[9];
ScatterGatherDesc[2].destEndPtr =(unsigned int)&destPtr3[9];
ScatterGatherDesc[2].ctrlCfg.src_inc = 0;
ScatterGatherDesc[2].ctrlCfg.dst_inc = 0;
ScatterGatherDesc[2].ctrlCfg.src_size = 0;
ScatterGatherDesc[2].ctrlCfg.n_minus_1 = 9u;
ScatterGatherDesc[2].ctrlCfg.r_power = 0;
ScatterGatherDesc[2].ctrlCfg.cycle_ctrl = 2u;
/* the last descriptor has to be ARQ to stop the DMA */
/* enable the DMA controller */
*pREG_DMA0_CFG = 1u;
/* Enable the DMA channel 16 */
*pREG_DMA0_EN_SET = (1u << 16u);
/* Enable the peripherals to create DMA requests on channel 16 */
*pREG_DMA0_RMSK_CLR = (1u << 16u);
/* give the address of the built channel descriptor to the DMA controller */
*pREG_DMA0_PDBPTR = (unsigned int) &ChannelDesc[0];
/* Locate the Alternate channel descriptor for channel 16 in memory */
pChannelDescAlternate =(ADI_DMA_DESC*)((*pREG_DMA0_ADBPTR) + (16 * 16));
uint8_t* ptypIntScatterGatherDesc =(uint8_t*)pScatterGatherDesc;
uint8_t* ptypIntChannelDescAlternate =(uint8_t*)pChannelDescAlternate;
/* Build the primary channel descriptor to move ScatterGatherDesc to ChannelDescAlternate*/
ChannelDesc[16].srcEndPtr =(unsigned int)((ptypIntScatterGatherDesc + 11*4)+3);
ChannelDesc[16].destEndPtr =(unsigned int)((ptypIntChannelDescAlternate + 3*4)+3);
ChannelDesc[16].ctrlCfg.src_inc = 2;
ChannelDesc[16].ctrlCfg.dst_inc = 2;
ChannelDesc[16].ctrlCfg.src_size = 2;
ChannelDesc[16].ctrlCfg.n_minus_1 = 11u;
ChannelDesc[16].ctrlCfg.r_power = 2;
ChannelDesc[16].ctrlCfg.cycle_ctrl = 4u;
/* Generate a software DMA request on channel 16 */
*pREG_DMA0_SWREQ = (1u << 16u);
乒乓球模式
乒乓模式對于連續傳輸數據非常有用,在傳輸過程中沒有任何中斷。在此模式下,DMA 控制器在主描述符和備用描述符之間切換,直到控制器命中基本模式描述符。
最初,主數據結構和備用數據結構都填充了 DMA 描述符信息。傳輸從主數據結構開始。傳輸完成后,DMA 控制器會立即選取備用數據結構并開始下一個事務,而不會延遲切換。備用傳輸完成后,傳輸將返回到主數據結構,并且循環繼續,直到遇到基本模式描述符。
確保當主數據結構完成且備用數據結構正在傳輸時,在備用數據結構完成傳輸之前重置主數據結構,反之亦然,重置備用數據結構。
如前所述,當數據結構完成事務時,除非更新源字段和目標字段,否則只有 N ? 1 字段和循環控制字段會重置。
乒乓球模式的編程順序如下:
使用有關在基本模式下使用最后一個 DMA 描述符執行的傳輸的信息定義所有 DMA 描述符(如果需要 DMA 事務終止)。
將填充的主描述符和備用描述符復制到主數據結構和備用數據結構。
將 PCD 指針指向主通道描述符的基座。
清除特定通道的通道請求掩碼。
啟用 DMA 的請求源中斷和 DMA 完成中斷。
啟用相應的 DMA 通道。
通過將DMA_CFG寄存器設置為 1 來啟用 DMA 控制器。
生成軟件 DMA 請求(如果是軟件乒乓 DMA 請求)或等待外圍 DMA 請求生成。
在DMA_DONE中斷例程中,創建一個標志,指示哪個主傳輸或備用傳輸已完成。因此,在特定傳輸完成后,重置通道的主描述符或備用描述符中的相應字段。
乒乓球模式的時序圖如圖 4 所示。
圖4.乒乓球模式時序圖
示例代碼:使用UART進行內存乒乓球數據傳輸的內存外圍設備
此示例演示如何執行從內存塊到外設緩沖區寄存器的乒乓硬件請求傳輸。該示例使用通道 9 DMA (UART0 Rx),ping 和 pong 交替將從 UART 接收的數據分別存儲到 srcPtr[] 和 destPtr[] 中。當從 UART 收到五個字符時,ping 將切換到 pong,反之亦然。然后,收到的五個字符通過UART發送出去。
/* Build the primary channel descriptor */
ChannelDesc[9].srcEndPtr =(unsigned int) pREG_UART0_RX;
ChannelDesc[9].destEndPtr =(unsigned int)&srcPtr[4];
ChannelDesc[9].ctrlCfg.src_inc = 3;
ChannelDesc[9].ctrlCfg.dst_inc = 0;
ChannelDesc[9].ctrlCfg.src_size = 0;
ChannelDesc[9].ctrlCfg.n_minus_1 = 4u;
ChannelDesc[9].ctrlCfg.r_power = 0;
ChannelDesc[9].ctrlCfg.cycle_ctrl = 3u;
/* enable the DMA controller */
*pREG_DMA0_CFG = 1u;
/* Enable the DMA channel 9 */
*pREG_DMA0_EN_SET = (1u << 9u);
/* Enable the peripherals to create DMA requests on channel 9 */
*pREG_DMA0_RMSK_CLR = (1u << 9u);
/* give the address of the built channel descriptor to the DMA controller */
*pREG_DMA0_PDBPTR = (unsigned int) &ChannelDesc[0];
/* Locate the Alternate channel descriptor for channel 9 in memory */
ChannelDescAlternate =(ADI_DMA_DESC*)((*pREG_DMA0_ADBPTR) + (16 * 9));
/* Build the Alternate channel descriptor */
ChannelDescAlternate->srcEndPtr =(unsigned int)pREG_UART0_RX;
ChannelDescAlternate->destEndPtr =(unsigned int)&destPtr[4];
ChannelDescAlternate->ctrlCfg.src_inc = 3;
ChannelDescAlternate->ctrlCfg.dst_inc = 0;
ChannelDescAlternate->ctrlCfg.src_size = 0;
ChannelDescAlternate->ctrlCfg.n_minus_1 = 4u;
ChannelDescAlternate->ctrlCfg.r_power = 0;
ChannelDescAlternate->ctrlCfg.cycle_ctrl = 3u;
/* Enable the DMA channel 9 interrupt in NVIC */
NVIC_EnableIRQ(DMA0_CH9_DONE_IRQn);
/* pin mux for UART 0 */
*((volatile uint32_t *)REG_GPIO0_CFG) |= UART0_TX_PORTP0_MUX | UART0_RX_PORTP0_MUX ;
/* UART 0 configuration */
/* baud rate = 9600*/
*pREG_UART0_DIV = 0x1C;
*pREG_UART0_FBR = (0x1<
審核編輯:郭婷
-
控制器
+關注
關注
112文章
16361瀏覽量
178051 -
存儲器
+關注
關注
38文章
7492瀏覽量
163836 -
cpu
+關注
關注
68文章
10863瀏覽量
211765
發布評論請先 登錄
相關推薦
評論