DSP

C6000 EDMA 使用心得总结

2019-07-13 12:53发布

转载自左超的CSDN博客http://blog.csdn.net/henhen2002/article/details/4464166 几天调下来,总感觉TI的文档语焉不详。不过想想也对,250页的文档是谁都要写到郁闷的。而且一个懂了的人给初学者讲东西总会自然的略过一些看似当然的关键。
     这几天遇到的问题就是EDMA可以工作,却不能连续不断的转起来。而问题的所在就在于文档没读清楚。
     文档(SPRU234,下同)在第一章的第一节的图1-2就给出了EDMA的控制块图。现在看到控制块图当然联想到实际的工作流程,但是似乎文档并没有解释EDMA的工作流程。
     在EDMA工作中,总共使用了两个参数表,其中的一个是由EDMA控制器维护的,而另一个是由用户维护的。EDMA控制器维护的参数表规则的排列在整个参数表的开始部分,一个通道一个,谁也不多谁也不少。而用户维护的参数表则被称作Reload channel parameters,随意分布在整个参数表的后半部分。现在看来,能知道EDMA控制器是用了两个参数表就可以很好的理解EDMA的工作方式。
     非连续的工作:
     EDMA可以工作在连续的和非连续的状态。数据非连续的时候可以采用单次的非连续工作状态。这时用户在初始化的时候直接初始化EDMA所维护的参数表就可以了。如下面的例子:
*edmaHandle = EDMA_open(eventId, EDMA_OPEN_RESET);
if(*edmaHandle == EDMA_HINV)
   test_exit(FAIL);

if((tcc = EDMA_intAlloc(-1)) == -1)
   test_exit(FAIL) ;

EDMA_configArgs(
   *edmaHandle,
   EDMA_OPT_RMK(
   EDMA_OPT_PRI_MEDIUM,
   EDMA_OPT_ESIZE_32BIT,
   EDMA_OPT_2DS_NO,
   EDMA_OPT_SUM_NONE,
   EDMA_OPT_2DD_YES,
   EDMA_OPT_DUM_INC,
   EDMA_OPT_TCINT_YES,
  
   EDMA_OPT_TCC_OF(tcc & 0xF),
   EDMA_OPT_TCCM_OF(((tcc & 0x30) >> 4)),
   EDMA_OPT_ATCINT_NO,
  
   EDMA_OPT_ATCC_OF(0),
   EDMA_OPT_PDTS_DISABLE,
  
   EDMA_OPT_PDTD_DISABLE,
   EDMA_OPT_LINK_NO,
   EDMA_OPT_FS_NO
   ),
   EDMA_SRC_RMK(srcAddr),
   EDMA_CNT_RMK(EDMA_CNT_FRMCNT_OF((frameCount - 1)),
   EDMA_CNT_ELECNT_OF(elementCount)),
   EDMA_DST_RMK(dstAddr),
   EDMA_IDX_RMK(EDMA_IDX_FRMIDX_OF((elementCount * 4)),
   EDMA_IDX_ELEIDX_OF(0)),
  
   EDMA_RLD_RMK(EDMA_RLD_ELERLD_OF(elementCount), EDMA_RLD_LINK_OF(0))
);
     在这里,LINK参数是被忽略的,这一点可以从TI给的例子中看到(52页开始)。相对于很多DMA控制器来说,这些参数显得很简单而TI的讲述非常详细。
     连续工作方式:
     非连续的工作方式很好理解,TI也讲得很详细,而TI在讲述连续工作方式的时候却一笔带过。和非连续的方式不同之处在于用户需要初始化两张表,而只能维护其中的一张,另一张是由EDMA控制器自动维护的。其中的EDMA维护的这张表是在EDMA工作期间使用的,而用户维护的这张表是在EDMA开始新工作的时候重载的。
     用户在初始化阶段必须先初始化EDMA通道对应的参数表,之后在参数表的后半部分申请一张空闲的参数表,并将其初始化。如文档中1.16.4.3所示的例子,其中62页示例的就是通道对应的参数表,而63页示例的就是需要重载的参数表。
     EDMA在接受了这样的参数表之后,首先判断是否达成DMA完成条件(参考TI文档),如果未完成,开始将参数表中的ELECNT自减,减到零则 FRMCNT减一,而后从ELERLD参数中重载ELECNT,在开始新的一个Frame。周而复始。当FRMCNT也减完了则从重载表重新装入参数开始新一轮的工作。用户则可以根据工作需要在EDMA控制器重载参数之前对重载参数设置以开始不同的工作。
     下面的例子是TI在例子中给出的一个三张表的例子,根据这些例子,用户甚至可以使用4张表或者更多,最多可以单一通道使用22张表(C64X)。而多通道则可以使用N+21张表。

   hEdma = EDMA_open(EDMA_CHA_TINT1, EDMA_OPEN_RESET);

   hEdmaPing = EDMA_allocTable(-1);
   hEdmaPong = EDMA_allocTable(-1);

   cfgEdma = cfgEdmaPing;
  

   cfgEdmaPing.rld = EDMA_RLD_RMK(0,hEdmaPing);
   cfgEdmaPong.rld = EDMA_RLD_RMK(0,hEdmaPong);
   cfgEdma.rld      = EDMA_RLD_RMK(0,hEdmaPong);   
   EDMA_config(hEdma, &cfgEdma);   
   EDMA_config(hEdmaPing, &cfgEdmaPing);
   EDMA_config(hEdmaPong, &cfgEdmaPong);    下面是Ping和Pong的参数:
EDMA_Config cfgEdmaPing = {  
   EDMA_OPT_RMK(
     EDMA_OPT_PRI_LOW,
     EDMA_OPT_ESIZE_32BIT,
     EDMA_OPT_2DS_NO,
     EDMA_OPT_SUM_NONE,
     EDMA_OPT_2DD_NO,
     EDMA_OPT_DUM_INC,
     EDMA_OPT_TCINT_YES,
     EDMA_OPT_TCC_OF(TCCINTNUM),
     EDMA_OPT_TCCM_OF(0),
     EDMA_OPT_ATCINT_NO,
     EDMA_OPT_ATCC_OF(0),
     EDMA_OPT_PDTS_DEFAULT,
     EDMA_OPT_PDTD_DEFAULT,
     EDMA_OPT_LINK_YES,
     EDMA_OPT_FS_NO
   ),
   EDMA_SRC_OF(&ping_data),
   EDMA_CNT_OF(BUFF_SZ),
   EDMA_DST_OF(ping),
   EDMA_IDX_OF(0x00000004),
   EDMA_RLD_OF(0x00000000)
};                         
EDMA_Config cfgEdmaPong = {
   EDMA_OPT_RMK(
     EDMA_OPT_PRI_LOW,
     EDMA_OPT_ESIZE_32BIT,
     EDMA_OPT_2DS_NO,
     EDMA_OPT_SUM_NONE,
     EDMA_OPT_2DD_NO,
     EDMA_OPT_DUM_INC,
     EDMA_OPT_TCINT_YES,
     EDMA_OPT_TCC_OF(TCCINTNUM),
     EDMA_OPT_TCCM_OF(0),
     EDMA_OPT_ATCINT_NO,
     EDMA_OPT_ATCC_OF(0),
     EDMA_OPT_PDTS_DEFAULT,
     EDMA_OPT_PDTD_DEFAULT,
     EDMA_OPT_LINK_YES,
     EDMA_OPT_FS_NO
   ),
   EDMA_SRC_OF(&pong_data),
   EDMA_CNT_OF(BUFF_SZ),
   EDMA_DST_OF(pong),
   EDMA_IDX_OF(0x00000004),
   EDMA_RLD_OF(0x00000000)
};
     这样的方式很像是运动场的径赛场地,运动员从抽头跑入,然后一圈一圈的不断跑下去。使用这种方式似乎可以很好的避免内存访问冲突,我在调试过程中试图修改EDMA自动维护的表项时导致了很糟糕的内存访问错误。
                                   这是使用BIOS和CSL LIB 作EDMA简单例子. 在CCS BIOS 中需要作 中断--EDMA--edmaHwi 的设置. Spru234 和spra636a 都是有用的参考
解释见注释.
void main()
{
    initEdma(); //Edma 初始化
    
    initHwi(); //中断初始化

    EDMA_setChannel(hEdma); //开始EDMA传送
    while (1) {}
}
void initEdma(void)
{
EDMA_Config gEdmaConfig; //EDMA设置表
hEdma = EDMA_open(EDMA_CHA_ANY, EDMA_OPEN_RESET); //
gXmtTCC = EDMA_intAlloc(-1); //分配一个可用的TCC

.... //根据实际需要填写EDMA设置表
EDMA_config(hEdma, &gEdmaConfig); 设置EDMA通道

hEdmaReload = EDMA_allocTable(-1); //得到EDMA重载标实 EDMA_config(hEdmaReload, &gEdmaConfig); //设置EDMA重载通道

EDMA_link(hEdma, hEdmaReload); //设置EDMA重载
EDMA_link(hEdmaReload, hEdmaReload); //设置EDMA重载
EDMA_intClear(gXmtTCC); // 清除可能的EDMA中断
EDMA_intEnable(gXmtTCC); // 打开EDMA中断
EDMA_intHook(gXmtTCC, edmaHwi); //通知系统edmaHwi是中断服务者
}

void edmaHwi(int tcc)  //中断服务
{
。。。。//准备数据
EDMA_setChannel(hEdma); //再次开始EDMA传送
} 昨天搞了一天的EDMA乒乓操作学习,晚上临走的时候没有成功,主要是EDMA_Config cfgEdmaPing = { } 这个结构体不支持变量,今天早上和zz生完气就到实验室调成功了。(她总是因为小事朝我发火!!!)
    首先,从形式上看EDMA的乒乓操作分为两种
    1. 这种用EDMA_Config cfgEdmaPing = { } 构造PaRAM的方法,要求填充结构体的都为常量,适合已知地址的传输,我还有搞清楚VP口A通道的寄存器地址,第一次没有成功。
# EDMA_Handle hEdma;       
# EDMA_Handle hEdmaPing;   
# EDMA_Handle hEdmaPong;   
# EDMA_Config cfgEdma;    

#   
# EDMA_Config cfgEdmaPing = {    
#                           };                           
#   
#   
# EDMA_Config cfgEdmaPong = {  
#                           };          


#   hEdma = EDMA_open(EDMA_CHA_TINT1, EDMA_OPEN_RESET);    
#   hEdmaPing = EDMA_allocTable(-1);  
#   hEdmaPong = EDMA_allocTable(-1);
   
#   cfgEdma = cfgEdmaPing;  
#   cfgEdmaPing.rld = EDMA_RLD_RMK(0,hEdmaPing);  
#   cfgEdmaPong.rld = EDMA_RLD_RMK(0,hEdmaPong);  
#   cfgEdma.rld     = EDMA_RLD_RMK(0,hEdmaPong);    
#   EDMA_config(hEdma, &cfgEdma);       
#   EDMA_config(hEdmaPing, &cfgEdmaPing);  
#   EDMA_config(hEdmaPong, &cfgEdmaPong);
#   IRQ_enable(IRQ_EVT_EDMAINT);  //默认EDMA中断是关闭的  
#   EDMA_intEnable(TCCINTNUM);           
#   EDMA_enableChannel(hEdma); 
   2. 利用EDMA_configArgs()函数来构造PaRam的方法,我正是用的这个方法
    EDMA_Handle hEdmaTable_Ping;
    EDMA_Handle hEdmaTable_Pong;
   
    *edmaHandle = EDMA_open(eventId, EDMA_OPEN_RESET);
    if(*edmaHandle == EDMA_HINV)
    {
        //for(;;){}
    }   
   
    if((tcc = EDMA_intAlloc(-1)) == -1)
    {
        for(;;){}
    }
   
    hEdmaTable_Ping = EDMA_allocTable(-1);
    hEdmaTable_Pong = EDMA_allocTable(-1);
   
    EDMA_configArgs(
        *edmaHandle,
       
    );
    EDMA_configArgs(
        hEdmaTable_Ping,
       
    );
        EDMA_configArgs(
        hEdmaTable_Pong,
       
    );

   
    EDMA_link(*edmaHandle,hEdmaTable_Ping);
   
    EDMA_link(hEdmaTable_Ping,hEdmaTable_Pong);
    EDMA_link(hEdmaTable_Pong,hEdmaTable_Ping);//父参数->子参数
    *tccNum = tcc;

    总结一下:
EDMA传输一次完成之后,参数就会清空,会按照上述方式中kink的语句,将参数内容填充,一旦又有EDMA事件发生,就会按照事先加载的参数进行传输。 EDMA_Handle hEdma;
EDMA_Handle hEdmaTable;
...
hEdma = EDMA_open(EDMA_CHA_TINT1,0);
hEdmaTable = EDMA_allocTable(–1);
EDMA_link(hEdma,hEdmaTable);
EDMA_link(hEdmaTable,hEdmaTable);
  这是手册P161给出的例子,和合众达给出的例子差不多,我不明白为什么来了两个EDMA_link()函数,是不是因为这个没有在PRAM中设置连接,如果在参数中设置好了连接就不需要这两个EDMA_link()函数了。网上搜吧:
http://www.hellodsp.com/bbs/viewthread.php?action=printable&tid=6269
作者: luzhipei    时间: 2008-6-9 15:51     标题: EDMA传输

请问用于EDMA传输的CSL语句EDMA_LINK();的作用是什么?
是不是相当与把EDMA参数表中OPT的LINK位置1使LINK有效啊
作者: fanyu1224    时间: 2008-6-16 08:41

连接下一次工作模式的struct的句柄,
因为每一次一个通道工作完之后,通道的工作方式的配置就会被空
具体的参加SPRU401J的161页
作者: 玉丫子    时间: 2008-6-16 11:25

就是对它本身结构的链接
作者: luzhipei    时间: 2008-6-16 15:25

那么请问,使用了LINK语句后,是不是EDMA一次传输完成以后,如果还有同步事件到来又会触发一次EDNA传输?
作者: zhang4378030    时间: 2008-6-16 21:12
原帖由 luzhipei 于 2008-6-16 15:25 发表 
那么请问,使用了LINK语句后,是不是EDMA一次传输完成以后,如果还有同步事件到来又会触发一次EDNA传输?
你说的对,其实就是传输完了,把下一次的EDMA初始化好,如果在来一个事件就会启动EDMA进行数据传输 不知道我说明白了没有
作者: luzhipei    时间: 2008-6-17 08:55

那这样是不是和把EDMA参数表中OPT的LINK位置1使LINK有效,然后设置LINK的地址的效果是一样的?
作者: luzhipei    时间: 2008-6-19 09:38

我在实验中程序用了EDMA_LINK();但是发现只进了一次EDMA传输完成中断,FIFO不断有快满信号触发EDMA传输,,目的地址的波形可以不断更新显示,但是EDMA有了第一次传输完成中断后就没有进到中断函数中去了。哪位大侠能告诉小弟这是怎么回事啊?
作者: 玉丫子    时间: 2008-6-19 09:51

我在论坛中上传有SEED的EDMA的链接的工程,你可以去下载看看
作者: colortang    时间: 2008-6-19 16:11     标题: 回复 6# luzhipei 的帖子

貌似是这样的。
不过我感觉在传输完成后,这些参数还在,不需要重新设置LINK啊,望高人解答
作者: luzhipei    时间: 2008-6-20 10:28     标题: 回复 8# 玉丫子 的帖子

你好 请问你说的SEED的EDMA的链接的工程
是不是在资料区的合众达6713 EDMA实验例程啊
作者: initial5    时间: 2008-6-20 14:36     标题: 回复 1# luzhipei 的帖子

"This function links two EDMA transfers together by setting the LINK field of the
parent‘s RLD parameter appropriately. Both parent and child handles may be
from EDMA_open(), EDMA_allocTable(), or a combination of both.
parent->child
Note: This function does not attempt to set the LINK field of the OPT
parameter; this is still up to the user."
以上这段话来自于TI SPRU401的161页,意思应该是说EDMA_LINK()这个函数通过设置parent 参数表中的RLD的LINK部分的值为child参数表的地址的低16位将两个EDMA句柄(来源于EDMA_open()或 EDMA_allocTable()的返回值)链接起来。因为LINK操作能够成功的前提是参数表中OPT的LINK位被置位(见TI SPRU234的44页),因此最终能否实现LINK操作,还要由用户自己去配置OPT中的LINK位(置1即允许LINK操作)。
作者: luzhipei    时间: 2008-6-24 09:15

请问怎么样结束EDMA传输啊
是用EDMA_DISABLE()就可以了;还是将EDMA 链接到一个NULL的参数表?
作者: initial5    时间: 2008-6-25 14:56     标题: 回复 12# luzhipei 的帖子

//Stops the EDMA service.
void stopEdma(void)
{
    //Disable interrupts, close EDMA channel before exit of the program
    IRQ_disable(IRQ_EVT_EDMAINT);
    EDMA_RSET(CCER,0x00000000);
    EDMA_disableChannel(hEdma);
    EDMA_intDisable(TCCINTNUM);
    EDMA_intClear(TCCINTNUM);

        EDMA_close(hEdma);
        EDMA_resetAll();
        EDMA_RSET(CIPR,0xFFFFFFFF);
        EDMA_RSET(ECR,0xFFFFFFFF);
}

这是TI的一段例程,用于结束EDMA传输,希望能对LZ有帮助,EDMA_DISABLE()不是CSL函数。
作者: whx8880    时间: 2008-6-27 08:46

这种方法很全面,我都是链接到一个空的参数表
作者: ericxl06    时间: 2008-6-27 18:42

很多也不明白,受教了,学习中,