AN-1561:ADuCM4050的DMA编程

描述

直接存储器访问(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 块的主要部分。控制器从通道控制数据结构中获取传输信息,并在收到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<

审核编辑:郭婷

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分