DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接
控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备
开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。
S32K14x系列MCU有16个DMA通道,S32K11x系列只有4个DMA通道,而DMA请求却多达61个和多达两个常开槽,MADMUX实现将外设DMA请求(peripheral slots)指定到特定DMA通道(channels)。
DMAMUX有16个独立的通道路由;前4个通道还提供了一个触发器功能。
每个信道路由器都可以分配给其中一个可能的外围DMA插槽或其中一个始终打开的插槽。
3.1、禁用模式:此模式下,DMA channel 失能。因使能失能是在DMA配置寄存器中完成,该模式常用于DMA通道复位,或用于临时挂起DMA。
3.2、正常模式:此模式下,DMA源直接路由到特定DMA通道。
3.3、定期触发模式:此模式下,DMA源只能周期性地请求DMA传输,例如当发送缓冲区变空或接收缓冲区变满时,周期性地。
DMAMUX channels可以分为2类:
• Channels that implement the normal routing functionality plus periodic triggering capability–实现正常路由功能和周期性触发功能的通道;
• Channels that implement only the normal routing functionality–仅实现正常路由功能的通道。
4.1、含周期触发
除了正常的路由功能外,DMAMUX 的前 4 个通道提供特殊的周期性触发功能,可用于提供自动机制以固定间隔传输字节、帧或数据包,而无需处理器干预。触发由外部周期性中断定时器(例如PIT)产生; 这样,周期触发间隔的配置是通过配置外部周期定时器来完成的。
DMA 通道触发功能允许系统安排定期 DMA 传输,通常在某些外设的发送端进行,无需处理器干预。 此触发器的工作原理是将外设的请求门控到 DMA,直到触发事件发生。
DMA 请求得到服务后,外设将取消其请求,有效地重置门控机制,直到外设重新声明其请求并直到下一个触发事件。 这意味着如果观测到触发,但外设未请求传输,则该触发将被忽略。
此触发功能可用于支持 DMA 传输的任何外设,并且对两种情况最有用:
1、定期轮询特定总线上的外部设备 例如,将 SPI 的发送端分配给具有触发器的 DMA 通道。设置完成后,SPI 将请求 DMA 传输,大概来自内存,只要其发送缓冲区为空。通过在该通道上使用触发器,可以每 5 μs 自动执行一次 SPI 传输。在SPI的接收端,可以配置SPI和DMA将接收数据传输到内存中,有效地实现了一种无需处理器干预就可以周期性地从外部设备读取数据并将结果传输到内存中的方法。
2、使用 GPIO 端口驱动或采样波形 通过配置 DMA 将数据传输到一个或多个 GPIO 端口,可以使用存储在片上存储器中的表格数据创建复杂的波形。 相反,使用 DMA 从一个或多个 GPIO 端口定期传输数据,可以对复杂波形进行采样并将结果以表格形式存储在片上存储器中。
不含收起触发–Always-enabled DMA sources
除了可用作 DMA 源的外设外,还有 2 个额外的 DMA 源始终处于启用状态。
• 向/从 GPIO 执行 DMA 传输——从/向一个或多个 GPIO 引脚移动数据,或者不受限制(即尽可能快),或者定期(使用 DMA 触发功能)。
• 执行内存到内存的 DMA 传输——将数据从内存移动到内存,通常尽可能快,有时需要软件激活。
• 执行从内存到外部总线的 DMA 传输,反之亦然——类似于内存到内存的传输,这通常是尽快完成的。
• 任何需要软件激活的 DMA 传输——任何应该由软件明确启动的 DMA 传输。
5.1、内存映射
DMAMUX跟地址为:4002_1000h;
5.2、寄存器描述
通道寄存器(CHCFG0-CHCFG15)
寄存器功能:每个DMA通道都可以独立地启用/禁用,并与系统中的一个DMA插槽(外围插槽或始终打开的插槽)相关联。
寄存器位图及功能
功能描述
DMAMUX的主要目的是为系统使用可用的DMA通道提供灵活性。
在功能上,DMAMUX通道可分为两类:
①、实现正常路由功能和周期性触发功能的通道;
②、仅实现正常路由功能的通道;
eDMA 是一个二代模块,能够以较小的干扰实现对复杂数据搬运。其硬件微架构包含:
一个执行源和目的地址计算、数据移动操作的 DMA引擎;
包含每个16通道的 传输控制描述符的存储空间。
注:eDMA不支持等待模式;S32K11x系列不支持16字节突发和32字节突发
通道数:
eDMA模块主要分为两大部分:eDMA引擎、传输控制描述符(TCD)内存。
eDMA引擎进一步分为四大子模块:Address path、Data path、Program model/channel arbitration和Control。
传输控制描述符内存进一步分为两部分:Memory controller和Memory array。
eDMA 是一种高度可编程的数据传输引擎,经过优化,可最大限度地减少主机处理器所需的干预。它旨在用于要传输的数据大小静态已知且未在传输数据本身中定义的应用程序。
①、所有数据移动是通过双地址传输:从源代码读取,写入目标代码;
• 可编程的源、目的地址和大小
• 支持增强寻址模式
②、16通道实现,从主机处理器以最小的干预执行复杂的数据传输; 传输控制描述符(TCD) 被组织为支持两个深度,嵌套传输操作
• 每个通道的32字节 TCD存储在本地memory
• 内层数据通过minor次要的传输数量定义
• 外层数据通过major主要的传输数量定义
③、通道激活有如下三种方式
• 显式软件启动
• 通过通道到通道的链接机制发起连续传输
• 每个通道的外设请求
④、固定优先级和循环通道仲裁
⑤、通过可编程中断请求报告通道完成
• 每个通道一个中断,可以在主要迭代计数完成时断言
• 每个通道的可编程错误终止并在逻辑上加在一起形成一个错误中断到中断控制器
⑥、对分散/收集 DMA 处理的可编程支持
⑦、支持复杂的数据结构
正常模式、调试模式、等待模式;
①、CR-控制寄存器
②、DCHPRIn–通道优先级
fixed-priority:CR[ERCA] = 0时,通过DCHPRIn配置每个通道优先级,高优先级(数字越大)的先执行;
round-robin:忽略优先级,从通道高到低循环执行。
③、ERQ-使能请求寄存器
④、CERQ-清除使能请求寄存器
⑥、SERQ-设置使能请求寄存器
⑦、使能错误中断寄存器-Enable Error Interrupt Register (EEI)
作用:为每个通道使能错误中断。
0b - The error signal for corresponding channel does not generate an error interrupt
1b - The assertion of the error signal for corresponding channel generates an error interrupt request
⑧、清除使能错误中断寄存器-Clear Enable Error Interrupt Register (CEEI)
结构同CERQ,不过操作的是EEI.
⑨、设置使能错误中断寄存器-Set Enable Error Interrupt Register (SEEI)
结构同SERQ,不过操作的是EEI.
⑩、清除运行状态位寄存器-Clear DONE Status Bit Register (CDNE)
结构同CERQ,不过操作的是TCDn_CSR[DONE].
流程:
①、设置源地址–SADDR;
②、设置目的地址–DADDR;
③、设置源地址偏移–SOFF;
④、设置每次传输后,目的地址增加–DOFF;
⑤、设置源地址模数禁止,源数据位宽:DMA_BYTEn,目标地址模数禁止,目标数据位宽:DMA_BYTEn --ATTR;
⑥、设置当前主循环次数;
⑦、设置起始主循环次数;
⑧、设置通道每次传输字节数。
①、调整源地址的附加值;
②、调整目的地址的附加值;
③、设置带宽控制;
④、主循环结束后停止硬件请求;
⑤、主循环结束后产生中断
⑥、设置停止传输
/******************************************************************************
*函 数:DMA_MEM2MEM_Init
*功 能:DMA 内存到内存初始化
*参 数:
* DMA_CHn : 通道号(DMA_CH0 ~ DMA_CH15)
* SADDR : 源地址( (void * )&PTx_BYTEn_IN 或 (void * )&PTx_WORDn_IN )
* DADDR : 目的地址
* ptxn : 触发端口
* DMA_BYTEn : 每次DMA传输字节数
* count : 一个主循环传输字节数
*返回值:无
*备 注:无
*******************************************************************************/
void DMA_MEM2MEM_Init(DMA_CHn CHn, void *SADDR, void *DADDR, DMA_BYTEn byten, u32 count)
{
/* 开启DMA时钟 */
SIM->PLATCGC |= SIM_PLATCGC_CGCDMA(1);
if(PCC->PCCn[PCC_DMAMUX_INDEX] != PCC_PCCn_CGC_MASK)
PCC->PCCn[PCC_DMAMUX_INDEX] = PCC_PCCn_CGC_MASK; // CGC=1: Clock enabled for DMA
uint8_t BYTEs = (byten == DMA_BYTE1?1:(byten == DMA_BYTE2?2:(byten == DMA_BYTE4?4:16) ) ); //计算传输字节数
DMA->TCD[CHn].CSR &= 0xFFFFFFFF ^ DMA_TCD_CSR_DONE_MASK; // Clear Channel Done flag
/* 配置DMA通道的传输控制块 TCD ( Transfer Control Descriptor ) */
DMA_SADDR(CHn) = (uint32_t)SADDR; //设置源地址
DMA_DADDR(CHn) = (uint32_t)DADDR; //设置目的地址
DMA_SOFF(CHn) = BYTEs; //设置源地址偏移 = 0x0, 即不变
DMA_DOFF(CHn) = BYTEs; //每次传输后,目的地址加 BYTEs
DMA->TCD[CHn].ATTR = DMA_TCD_ATTR_SMOD(0) //源地址模数禁止 Source address modulo feature is disabled
| DMA_TCD_ATTR_SSIZE(byten) //源数据位宽:DMA_BYTEn 。 SSIZE = 0 -> 8-bit ,SSIZE = 1 -> 16-bit ,SSIZE = 2 -> 32-bit ,SSIZE = 4 -> 16-byte
| DMA_TCD_ATTR_DMOD(0) //目标地址模数禁止
| DMA_TCD_ATTR_DSIZE(byten); //目标数据位宽:DMA_BYTEn 。 设置参考 SSIZE
DMA->TCD[CHn].CITER.ELINKNO = DMA_TCD_CITER_ELINKNO_CITER(1) //当前主循环次数
| DMA_TCD_CITER_ELINKNO_ELINK(0); // The channel-to-channel linking is disabled
DMA->TCD[CHn].BITER.ELINKNO = DMA_TCD_BITER_ELINKNO_BITER(1) //起始主循环次数
| DMA_TCD_BITER_ELINKNO_ELINK(0); // The minor channel-to-channel linking is disabled
DMA->TCD[CHn].NBYTES.MLNO = DMA_TCD_NBYTES_MLNO_NBYTES(count); //count 通道每次传输字节数,这里设置为BYTEs个字节。注:值为0表示传输4GB */
/* 配置 DMA 传输结束后的操作 */
DMA_SLAST(CHn) = DMA_TCD_DLASTSGA_DLASTSGA(-count); //调整源地址的附加值,主循环结束后恢复 源地址
DMA->TCD[CHn].DLASTSGA = DMA_TCD_DLASTSGA_DLASTSGA(-count); //调整目的地址的附加值,主循环结束后恢复目的地址或者保持地址
DMA_CSR(CHn) = (0
| DMA_TCD_CSR_BWC(0) //带宽控制,每读一次,eDMA 引擎停止 8 个周期(0不停止;1保留;2停止4周期;3停止8周期)
// | DMA_TCD_CSR_DREQ_MASK //主循环结束后停止硬件请求
// | DMA_TCD_CSR_INTMAJOR_MASK //主循环结束后产生中断
| DMA_TCD_CSR_START(0) //停止传输
);
/* 开启中断 */
//DMA_EN(CHn); //使能通道CHn 硬件请求
//DMA_IRQ_EN(CHn); //允许DMA通道传输
}
①、设置目的地址;
②、使能通道CHn 硬件请求;
③、设置开始传输
/******************************************************************************
*函 数:DMATransDataStart
*功 能:开启DMA传输
*参 数:
* DMA_CHn : 通道号(DMA_CH0 ~ DMA_CH15)
* DADDR : 目的地址
*返回值:无
*备 注:无
*******************************************************************************/
static inline void DMATransDataStart(DMA_CHn CHn, uint32_t address)
{
DMA->TCD[CHn].DADDR = (uint32_t)address; //目的地址
DMA->ERQ |= (DMA_ERQ_ERQ0_MASK<<(CHn)); //使能通道CHn 硬件请求
DMA->TCD[CHn].CSR |= DMA_TCD_CSR_START(1); //开始传输
}
test.c
/******************************************************************************
*函 数:Test_DMA_Mem2Mem
*功 能:测试DMA 内存到内存
*参 数:无
*返回值:无
*备 注:无
*******************************************************************************/
void Test_DMA_Mem2Mem(void)
{
char S_data[]="Hello World!";
char D_data[13*2];
uartInit(UART0, 115200);
printf("\n\r-----------DMA内存到内存测试-----------\n\r");
printf("\n\r内存1:%s", S_data);
DMA_MEM2MEM_Init(DMA_CH0, (void *)S_data,(void *)D_data, DMA_BYTE1, 13); //初始化DMA
DMATransDataStart(DMA_CH0, (uint32_t)&D_data); //开始传输
delayms(10);
printf("\n\r内存2:%s", D_data);
while(1);
}
项目的仿真和PCB工程已经放在下面公众号里面,可以关注公众号:Kevin的学习站,输入关键字:“S32K14X-DMA”,就可以免费获取啦!创作不易,但您的点赞、关注、收藏就是对我最大的鼓励!