DM642 EDMA使用总结
2019-07-13 14:37发布
生成海报
最近在项目研发过程中使用到了DM642 的EDMA的连续传输方式,在网上查找一些相关资料,但是发现都没有系统的对EDMA的配置进行深入的说明,以至于将相关方法用在实验当中,证明是失败的,于是乎,求人不如求已,拿着datasheet慢慢的啃和不断反复的实验了(过程是痛苦,确又充实的),功夫不负有心人,至此,已将项目中的EDMA模块顺利调通!现将使用方法进行详细总结,以为后面同仁抛砖引玉,这里以EDMA搬运AD转换后的数字图像信号为例进行说明。
一.EDMA概念:
EDMA(Enhanced Direct Memory Access)是指在TMS320C6x11/C64xDSP中,在没有CPU参与的情况下,实现数据在DSP的各个存储器(个别的外设寄存器,如本例的视频口寄存器)之间转移的一个功能部件,它是数字信号处理器(DSP)中用于快速数据交换的重要技术,具有独立于CPU的后台批量数据传输的能力,能提供超过2Gb/s的外部带宽.支持64路独立触发的事件传输,总共有85个参数对“Linking”或“Chaining”进行配置。Linking是在1个事件被触发时允许1个序列进行传输。Chaining是当1个通道的数据传输完毕时触发另1个通道的数据传输。Linking和Chaining使得仅仅被CPU初始配置之后EDMA能够连续的自动运行。EDMA的特点决定了其适合图像处理的功能。由于图像处理的原始数据量很大.同时图像处理中也会产生同等量的中间数据.对于片内存储资源有限的高速DSP来说,一般需要借用外部存储空间。为了提高系统的实时处理能力.可以将数据在不同存储空间转移的任务交给EDMA来完成,而CPU只用于数据的计算。同时,EDMA对数据重排功能可以优化图像数据在内存中的存储,这不仅可以提高内部存储空间的利用效率.而且可以提高数据的传输速率。虽然对图像数据的传输也可由软件实现,但将消耗大量的CPU时钟周期。使DSP的高速性能难以发挥。而由EDMA来完成同样的工作几乎不占用CPU的时钟周期。
在C64xDSP中,EDMA控制器和DMA在结构上有很大的不同.其增强之处包括:
(1)提供了64个通道,每个通道对应一个外部触发或者软件触发的事件;
(2)通道间的优先级可以设置,可以根据缓急程度对不同事件配置不同优先级的参数;
(3)支持不同结构数据传输的链接。
EDMA控制器由以下几部分组成;
1)事件和中断处理寄存器;
2)事件编码器;
3)参数RAM;
4)硬件地址产生。
其中,事件寄存器控制对EDMA事件进行捕获。1个事件相当于1个同步信号,由它触发1个EDMA通道开始数据传输。如果有多个事件同时发生.则由事件编码器对它进行分辨。EDMA的参数RAM中存放了有关的传输参数(对EDMA配置主要是对RAM中参数进行配置,这些参数会被送入地址发生器硬件.进而产生搬运操作所需要的地址。
EDMA支持8bit、16bit和32bit数据的存储。在EDMA中定义了下列概念:
(1)数据单元(element)的传输。单个数据单元从源地址向目的地址传输.如果需要,每1个数据单元都可以由同步事件触发传输;
(2)帧(frame)。l组数据单元组成1帧,l帧中的数据单元可以是相邻连续存放的,也可以是间隔存放的.帧传输可以选择是否受同步事件控制,“帧”一般在1维传输中提及,(由于一维传输和二维传输的区分容易混淆,至少对于我而言,因此强烈建议,对于不是很复杂的传输,理解和使用一维传输就够了,没必要在这个概念上钻牛角尖,这样容易钻入死胡同,其实当你对一维传输理解透了,所谓的二维也很容易理解了,正所谓“融会贯通",因此本文只以一维传输为例);
(3)阵列(array)。1组连续的数据单元组成1个阵列.在1个阵列中的数据单元不允许间隔存放。1个阵列的传输可以选择是否受同步事件控制。“阵列”一般在2维传输中提及;
(4)块(block)。多个帧或者多个阵列的数据组成1个数据块;
(5)1维(1一D)传输。多个数据帧组成1个1维的数据传输。Block中帧的个数可以是l~65536。
(6)2维(2一D)传输。多个数据阵列组成1个2维的数据传输。第1维是阵列中的数据单元,第2维是阵列的个数。
二.EDMA传输控制机制
EDMA有64个通道.每1个通道都有1个事件与之关联.由这些事件触发相应通道的传输。EDMA控制器是基于RAM结构。参数.RAM(Parameter RAM。PaRAM)的容量是2KB,总共可以存放85组EDMA传输控制参数。多组参数还可以彼此连接起来,从而实现某些负责数据流的传输.例如循环缓存和数据排序等。参数RAM中保存的内容包括:
(1)64个EDMA通道对应的入口传输参数.每组参数包括6个字(24字节);
(2)21个用于重加载,链接的传输参数组。每组参数包括6个字(24字节),这21个重载参数组地址紧接着上面第64个通道参数的地址;
(3)8字节空余的RAM可以作为“草稿区”(scratchpad area)。
一旦捕获到某个事件.控制器将从PaRAM顶部的64组入口参数中读取数据对应的控制参数送往地址发生器硬件。
下图给出1组EDMA传输参数的内部结构,总共6个字.192bit。可以通过32bit的外设总线对EDMA的参数.RAM进行访问。 通常在应用程序当中我们使用TI提供的CSL库函数进行配置,除非在程序启动阶段之初,C语言环境还没建立时需要使用EDMA时,才通过对外设总线对EDMA参数进行访问配置,这种使用情况非常少,这里不作说明。
下面对上图的RAM结构进行说明:
(1)可选参数(Option Parameter),32bit,各个字段如下图所示:
1)PRI:EDMA事件优先级,数组范围如下图:
共有四个优先级:0级是最迫切的优先级,1是高优先级,2为中优先级,3是最低优先级,各个通道事件根据事件的紧迫程度可以设置上面四种之一的优先级。一般情况设置成中优先级:010.
2)ESIZE:设置传输数据单元.EDMA支持对8位、16位、32位的数据的存储,取值分别为:10,01,00,默认为00 本项目,中对图像灰度信号采用8位的量化,DSP的外部数据线为32位的,于是,可以把4个像素的灰度值合并成一个数据单元(element),共计32位,因此,RAM参数中的ESIZE位设置为00b
(未完待续)
以下提供一份网上查找的通过外设总线对EDMA进行配置的程序(在C语言环境还没初始化时必须使用这种方法)
//TI公司的TMS320C6416上的EDMA用作数据传输,通过下面程序可以清楚了解EDMA的控制方法。
#include
//定义EDMA控制寄存器地址 开始
#define EPRH 0x01A0FF9C
#define CIPRH 0x01A0FFA4
#define CIERH 0x01A0FFA8
#define CCERH 0x01A0FFAC
#define ERH 0x01A0FFB0
#define EERH 0x01A0FFB4
#define ECRH 0x01A0FFB8
#define ESRH 0x01A0FFBC
#define EPRL 0x01A0FFDC
#define CIPRL 0x01A0FFE4
#define CIERL 0x01A0FFE8
#define CCERL 0x01A0FFEC
#define ERL 0x01A0FFF0
#define EERL 0x01A0FFF4
#define ECRL 0x01A0FFF8
#define ESRL 0x01A0FFFC
//定义EDMA控制寄存器地址 结束
//定义EDMA参数RAM中通道10的相关地址 开始
#define PaRAM10OPT 0x01A000F0//Options(OPT)
#define PaRAM10SRC 0x01A000F4//SRC Address(SRC)
#define PaRAM10count 0x01A000F8//Array/frame count(FRMCNT)|Element count(ELECNT)
#define PaRAM10DST 0x01A000FC//DST address(DST)
#define PaRAM10index 0x01A00100//Array/frame index(FRMIDX)|Element index(ELEIDX)
#define PaRAM10RDLK 0x01A00104//Element count reload(ELERLD)|Link address(LINK)
//定义EDMA参数RAM中通道10的相关地址 结束
//定义EDMA参数RAM中通道11的相关地址 开始
#define PaRAM11OPT 0x01A00108//Options(OPT)
#define PaRAM11SRC 0x01A0010C//SRC Address(SRC)
#define PaRAM11count 0x01A00110//Array/frame count(FRMCNT)|Element count(ELECNT)
#define PaRAM11DST 0x01A00114//DST address(DST)
#define PaRAM11index 0x01A00118//Array/frame index(FRMIDX)|Element index(ELEIDX)
#define PaRAM11RDLK 0x01A0011C//Element count reload(ELERLD)|Link address(LINK)
//定义EDMA参数RAM中通道11的相关地址 结束
void main()
{
#define len1 16//一维传输块1长度
#define len2 16//一维传输块2长度
#define r 6//二维传输块行数
#define c 2//二维传输块列数
unsigned short TrLen1=0;//传输块1实际数据传输长度
unsigned short TrLen2=0;//传输块2实际数据传输长度
unsigned short Trr=0;//二维传输块实际传输行数
unsigned short Trc=0;//二维数据块实际传输列数
unsigned short error1=0;//定义数据块1传输错误标志
unsigned short error2=0;//定义数据块2传输错误标志
unsigned short error2D=0;//定义二维数据块传输错误标志
unsigned short NumOfEvent=0;//使用通道数
unsigned short D=0;//传输数据维数
int i=0;
int j=0;
//定义一维传输数据块1
unsigned int src1[len1]={0xAACCFF00,0xAACCFF00,0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,0xAACCFF00,0xAACCFF00};
//定义一维传输数据块2
unsigned int src2[len2]={0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,
0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,
0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,
0xBBDDEE22,0xBBDDEE22,0xBBDDEE22,0xBBDDEE22};
//定义二维传输数据块
unsigned int src3[r][c]={0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00,
0xAACCFF00,0xAACCFF00};
//声明目的段地址1、2、3。为了利于比较,所以赋予初值0。
unsigned int dst1[len1]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned int dst2[len2]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned int dst3[r][c]={0,0,
0,0,
0,0,
0,0,
0,0,
0,0};
//情况6:利用通道11传输4*2个32bit的二维数据,数据块整体传输
//PRI ESIZE 2DS SUM 2DD DUM TCINT TCC rsvd TCCM ATCINT rsvd ATCC rsvd PDTS PDTD LINK FS
//010 00 1 01 1 01 0 0000 0 00 0 0 000000 0 0 0 0 1
*(unsigned int*)(PaRAM11OPT)=0x43A00001;
*(unsigned int*)(PaRAM11SRC)=(unsigned int)&src3[0];
*(unsigned int*)PaRAM11count=0x00030002;
*(unsigned int*)PaRAM11DST=(unsigned int)&dst3[0];
*(unsigned int*)PaRAM11index=0x00000000;
*(unsigned int*)PaRAM11RDLK=0x00000000;
Trr=4;
Trc=2;
D=2;
//配置EDMA的控制寄存器 开始
*(unsigned int*)EPRL=0x00000C00;
*(unsigned int*)CIPRL=0x00000000;
*(unsigned int*)CIERL=0x00000C00;
*(unsigned int*)CCERL=0x00000C00;
*(unsigned int*)EERL=0x00000C00;
*(unsigned int*)ERL=0x00000C00;
*(unsigned int*)ESRL=0x00000800;//触发通道11
//配置EDMA的控制寄存器 结束
//等待EDMA数据传输结束 开始
i=0;
while(dst1[TrLen1-1]!=src1[TrLen1-1])//等待数据段1传输结束
{
if(i==1000)//出错检验
{
error1=1;
break;
}
i++;
}
i=0;
while(dst2[TrLen2-1]!=src2[TrLen2-1])//等待数据段2传输结束
{
if(i==1000)//出错检验
{
error2=1;
break;
}
i++;
}
i=0;
while(dst3[Trr-1][Trc-1]!=src3[Trr-1][Trc-1])//等待二维数据段传输结束
{
if(i==1000)//出错检验
{
error2D=1;
break;
}
i++;
}
//等待EDMA数据传输结束 结束
//输出源和目的数据 开始
if(D==1)//传输数据是一维
{
switch(NumOfEvent)//根据利用通道数分情况输出结果
{
case 1://只使用了通道11
if(error1==0)//无错时输出源和目的数据
{
printf("数据段1传输长度是:%d
",TrLen1);//打印实际传输长度
for(i=0;i
{
printf("%d SRC1:%8X DST1:%8X
",i+1,src1[i],dst1[i]);
}
}
else//出错时报错
{
printf("数据段1传输出错!
");
error1=0;
}
break;
case 2: //通道10和通道11都被使用
if(error1==0&&error2==0)//无错时输出源和目的数据
{
printf("数据段1传输长度是:%d
",TrLen1);//打印实际传输长度
for(i=0;i
{
printf("%d SRC1:%8X DST1:%8X
",i+1,src1[i],dst1[i]);
}
printf("数据段2传输长度是:%d
",TrLen2);//打印实际传输长度
for(i=0;i
{
printf("%d SRC2:%8X DST2:%8X
",i+1,src2[i],dst2[i]);
}
}
else//出错时报错
{
if(error1!=0)//数据段1传输出错
{
printf("数据段1传输出错!
");
error1=0;
}
if(error2!=0)//数据段2传输出错
{
printf("数据段2传输出错!
");
error2=0;
}
}
break;
default: printf("传输出错!
");
}
}
else if(D==2)//传输数据是二维
{
if(error2D==0)//传输无错,输出数据
{
printf("传输数据段维数:%d*%d
",Trr,Trc);
for(i=0;i
{
printf("%d SRC3:",i+1);
for(j=0;j
{
printf("%8X ",src3[i][j]);//按列输出源数据
}
printf(" DST3:");
for(j=0;j
{
printf("%8X ",dst3[i][j]);//按列输出目的数据
}
printf("
");//每行结束后换行
}
}
else//传输出错,报错
{
printf("二维数据传输出错!
");
error2D=0;
}
}
//输出源和目的数据 结束
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮