几天调下来,总感觉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);
/* allocate TCC for Y event */
if((tcc = EDMA_intAlloc(-1)) == -1)
test_exit(FAIL) ;
/* Configure EDMA parameters */
EDMA_configArgs(
*edmaHandle,
EDMA_OPT_RMK(
EDMA_OPT_PRI_MEDIUM, /* medium priority */
EDMA_OPT_ESIZE_32BIT, /* Element size 32 bits */
EDMA_OPT_2DS_NO, /* 1-dimensional source(FIFO) */
EDMA_OPT_SUM_NONE, /* fixed src address mode(FIFO) */
EDMA_OPT_2DD_YES, /* 2-dimensional destination */
EDMA_OPT_DUM_INC, /* destination increment */
EDMA_OPT_TCINT_YES, /* Enable transfer complete */
/* indication */
EDMA_OPT_TCC_OF(tcc & 0xF),
EDMA_OPT_TCCM_OF(((tcc & 0x30) >> 4)),
EDMA_OPT_ATCINT_NO, /* Disable Alternate Transfer */
/* Complete Interrupt */
EDMA_OPT_ATCC_OF(0),
EDMA_OPT_PDTS_DISABLE, /* disable PDT(peripheral device */
/* transfer) mode for source */
EDMA_OPT_PDTD_DISABLE, /* disable PDT mode for dest */
EDMA_OPT_LINK_NO, /* Enable linking */
EDMA_OPT_FS_NO/* NO="Array" YES="Block" synchronization */
),
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)), /* note: 32-bit element size */
/* no RLD in 2D and no linking */
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);
/*然后申请两个参数表,用于PING-PONG模式*/
hEdmaPing = EDMA_allocTable(-1);
hEdmaPong = EDMA_allocTable(-1);
/*令Edma所维护的参数表的参数与Ping的相同*/
cfgEdma = cfgEdmaPing;
/*初始化重载列表,注意EDMA所维护的列表指向了Pong,而Ping、Pong的表分别指向了自身,这样Edma首先以Ping模式工作(因为其参数与Ping相同),而后以Pong模式工作*/
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)
};
/* Create the EDMA configuration structure for pong transfers */
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自动维护的表项时导致了很糟糕的内存访问错误。
就这样吧,可能不是很清晰,不断修改中。
|