C6748_UART_EDMA

来源:互联网 发布:立石和 知乎 编辑:程序博客网 时间:2024/06/05 19:49

通过EDMA来实现UART的收发,可以减轻CPU的负担。主函数如下:

intmain(void){    // 外设使能配置    PSCInit();     // DSP 中断初始化    InterruptInit();     // EDMA3 中断初始化    EDMA3InterruptInit();     // EDMA3 初始化    EDMA3UARTInit(); // 初始化串口终端使用串口2UARTStdioInit(); // 申请串口 EDMA3 发送通道EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX,EVT_QUEUE_NUM); // 注册回调函数cb_Fxn[EDMA3_CHA_UART2_TX] = &callback; // 申请串口 EDMA3 接收通道EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,EDMA3_CHA_UART2_RX, EDMA3_CHA_UART2_RX,EVT_QUEUE_NUM); // 注册回调函数cb_Fxn[EDMA3_CHA_UART2_RX] = &callback; volatilechar enter[] = "Tronlong UART2 EDMA3 Application......\n\rPlease Enter 20 bytes from keyboard\r\n";volatilechar buffer[RX_BUFFER_SIZE];unsignedint buffLength = 0; // 发送数据buffLength = strlen((constchar *)enter);UartTransmitData(EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX, enter, buffLength); // 使能串口 DMA 模式UARTDMAEnable(SOC_UART_2_REGS, UART_RX_TRIG_LEVEL_1 | \UART_DMAMODE | \UART_FIFO_MODE ); // 等待从回调函数返回while(flag == 0);flag = 0; // 接收数据UartReceiveData(EDMA3_CHA_UART2_RX, EDMA3_CHA_UART2_RX, buffer); // 使能串口 DMA 模式UARTDMAEnable(SOC_UART_2_REGS, UART_RX_TRIG_LEVEL_1 | \UART_DMAMODE | \UART_FIFO_MODE ); // 等待从回调函数返回while(flag == 0);flag = 0; // 发送数据UartTransmitData(EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX, buffer, RX_BUFFER_SIZE); // 使能串口 DMA 模式UARTDMAEnable(SOC_UART_2_REGS, UART_RX_TRIG_LEVEL_1 | \UART_DMAMODE | \UART_FIFO_MODE ); // 等待从回调函数返回while(flag == 0);flag = 0; // 释放 EDMA3 通道EDMA3FreeChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,EDMA3_CHA_UART2_TX, EDMA3_TRIG_MODE_EVENT,EDMA3_CHA_UART2_TX, EVT_QUEUE_NUM); EDMA3FreeChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,EDMA3_CHA_UART2_RX, EDMA3_TRIG_MODE_EVENT,EDMA3_CHA_UART2_RX, EVT_QUEUE_NUM);     // 主循环    for(;;)    {     }}主函数中,先是对EDMA3中断初始化,EDMA3InterruptInit();,函数如下:voidEDMA3InterruptInit(void){    IntRegister(C674X_MASK_INT4, Edma3ComplHandlerIsr);    IntRegister(C674X_MASK_INT5, Edma3CCErrHandlerIsr);     IntEventMap(C674X_MASK_INT4, SYS_INT_EDMA3_0_CC0_INT1);    IntEventMap(C674X_MASK_INT5, SYS_INT_EDMA3_0_CC0_ERRINT);     IntEnable(C674X_MASK_INT4);    IntEnable(C674X_MASK_INT5);}


函数中,注册了4号和5CPU可屏蔽中断C674X_MASK_INT4C674X_MASK_INT5的服务函数,分别为Edma3ComplHandlerIsrEdma3CCErrHandlerIsr,然后将8号中断事件SYS_INT_EDMA3_0_CC0_INT1EVT8)映射到C674X_MASK_INT4,将56号中断事件SYS_INT_EDMA3_0_CC0_ERRINTEVT56)映射到C674X_MASK_INT5,并使能这两个CPU可屏蔽中断。SYS_INT_EDMA3_0_CC0_INT1EDMA3_0模块的通道控制器0影子区域1传输完成的中断标志,6748有两个EDMA3模块,分别为EDMA3_0EDMA3_1,每个EDMA_n模块都只有一个通道控制器CC0channel controller 0)。

(手册P93~94

(指南P493

EDAM3_0_CC04个影子区域(shadow region),EDMA3_1_CC0也有4个影子区域。EDMA3CC通过将地址空间划分为多个区域,把通道资源分到这些区域,并把不同的区域划给不同EDMA使用者专用,可以让不同的使用者共用一块EDMA,却互不影响。

(指南P491

影子区域的寄存器有如下这些,通过影子通道区域的寄存器,可以对全局通道区域(global channel region)的通道寄存器(channel register)(包括DMAQDMA,和中断寄存器)进行访问。通过DMA区域访问使能寄存器(DMA region access enable registerDRAEm)和QDMA区域访问使能寄存器(QDMA region access enable registerQRAEm)可以控制影子区域寄存器对全局区域的通道寄存器的访问。

(指南P518

(指南P519

(手册P104

(手册P105

对每个EDMA3影子区域(EDMA3 shadow region)都有一集与该影子区域相关的寄存器(a set of registers),这一集寄存器可以将DMA/QDMA通道以及中断完成码与该影子区域关联起来,将DMA/QDMA通道以及TCC值的所有权赋予这个影子区域。每个影子区域都有一个DRAEm寄存器和一个QRAEm寄存器。每个DRAEm寄存器的位数与DMA通道的数目一致(match),都是32位寄存器,有效位32位,对应32DMA通道。每个QRAEm寄存器的位数与QDMA通道的数目一致,都是32位寄存器,有效位8位,对应8QDMA通道。需要对DRAEm寄存器进行设置,将DMA通道的所有权赋予相应的影子区域。DRAE可以过滤影子区域对DMA事件寄存器和中断寄存器的访问。只有DRAE中相应的位为1,对应的DMA/interrupt通道才是可以访问的,否则访问无效,对DMA/interrupt通道写会被丢弃,读则会返回0.

 

通常,会把QDMA/DMA通道唯一地赋予某个区域使用。这时,只有该影子区域的DRAE/QRAE寄存器中对应该通道的位被置位,其他影子区域的DRAE/QRAE寄存器中对应该通道的位都应被清0.另外,在每个影子区域,都有一个相关的影子区域完成中断(shadow region completion interruptEDMA3CC_INTnn表示影子区域号),对单核CPU,所有的影子区域中断都会连到CPU中断控制器上。每个影子区域的DRAE作为该影子区域中断的二级使能(a secondary interrupt enable)(一级中断使能在中断使能寄存器IER里,interrupt enable register)。

(指南P524

(指南P578

(指南P577

大部分DMA通道都已事先定好地与特定的硬件外设事件联系起来了,只有当该特定事件发生了,对应的DMA通道才会提出传输请求,其它事件对该通道没有影响。每个DMA通道所对应的事件如下图所示:

(mega手册P162)

EDMA3中断初始化完成后,就要进行EDMA3初始化了,EDMA3初始化函数为EDMA3UARTInit();代码如下:

voidEDMA3UARTInit(void){    EDMA3Init(SOC_EDMA30CC_0_REGS, EVT_QUEUE_NUM);}


EDMA3Init代码如下:

voidEDMA3Init(unsignedint baseAdd,unsignedint queNum){unsignedint count = 0;unsignedint i = 0; #ifdef _TMS320C6X/* For DSP, regionId is assigned here and used globally in the driver */regionId = (unsignedint)1u;#else/* FOR ARM, regionId is assigned here and used globally in the driver */regionId = (unsignedint)0u;#endif /* Clear the Event miss Registers */HWREG(baseAdd + EDMA3CC_EMCR) = EDMA3_SET_ALL_BITS;HWREG(baseAdd + EDMA3CC_EMCRH) = EDMA3_SET_ALL_BITS; HWREG(baseAdd + EDMA3CC_QEMCR) = EDMA3_SET_ALL_BITS; /* Clear CCERR register */HWREG(baseAdd + EDMA3CC_CCERRCLR) = EDMA3_SET_ALL_BITS; /* FOR TYPE EDMA*//* Enable the DMA (0 - 64) channels in the DRAE and DRAEH register */ HWREG(baseAdd + EDMA3CC_DRAE(regionId)) = EDMA3_SET_ALL_BITS;HWREG(baseAdd + EDMA3CC_DRAEH(regionId)) = EDMA3_SET_ALL_BITS;  if((EDMA_REVID_AM335X == EDMAVersionGet())){for(i = 0; i < 64; i++){/* All events are one to one mapped with the channels */HWREG(baseAdd + EDMA3CC_DCHMAP(i)) = i << 5;} }/* Initialize the DMA Queue Number Registers */for (count = 0;count < SOC_EDMA3_NUM_DMACH; count++){HWREG(baseAdd + EDMA3CC_DMAQNUM(count >> 3u)) &=EDMA3CC_DMAQNUM_CLR(count);HWREG(baseAdd + EDMA3CC_DMAQNUM(count >> 3u)) |=EDMA3CC_DMAQNUM_SET(count,queNum);} /* FOR TYPE QDMA *//* Enable the DMA (0 - 64) channels in the DRAE register */HWREG(baseAdd + EDMA3CC_QRAE(regionId)) = EDMA3_SET_ALL_BITS; /* Initialize the QDMA Queue Number Registers */for (count = 0;count < SOC_EDMA3_NUM_QDMACH; count++){HWREG(baseAdd + EDMA3CC_QDMAQNUM) &= EDMA3CC_QDMAQNUM_CLR(count);HWREG(baseAdd + EDMA3CC_QDMAQNUM) |=EDMA3CC_QDMAQNUM_SET(count,queNum);}}


因为_TMS320C6X是编译器已经预定义好的了,所以regionId=1.这里需要通过查看c6000系列编译器手册C6000 Optimizing C/C++ Compiler User's Guide的Using the C/C++ Compiler >Controlling the Preprocessor 这节,里面列出了C6000编译器预定义的宏,里面提到_TMS320C6X是always defined的。

该函数对DRAE1的所有位置1,即使能影子区域1EDMA3_0CC0所有32DMA通道的访问。当DMA通道对应的中断事件发生时,只要IER中对应该通道的位也使能了(一级中断使能),就会产生EDMA3_0_CC0_INT1中断(EDMA3_0 Channel Controller 0 Shadow Region 1 Transfer Completion Interrupt)。

(指南P524

(手册P93

(指南P591

然后该函数初始化EDMA3_0_CC0所有DMA通道号寄存器(DMA Channel Queue Number RegisterDMAQNUM),将4DMAQNUM寄存器都清为0,即所有通道的事件请求都放进队列0里。然后把QDMAQNUM寄存器也做同样的初始化操作,这样EDMA3初始化就完成了。

(指南P562

(指南P568

(指南P496

接着,主函数UARTStdioInit();语句函数如下:

voidUARTStdioInit(void){UARTConsoleInit();}


该函数在demo\StarterWare\Source\StarterWare\Utils路径下工程文件里的uartStdio.c程序中,该函数又调用了UARTConsoleInit函数,UARTConsoleInit函数为创龙特有程序,函数在Platform工程下的UARTConsole.c文件里,该函数初始化串口控制台,函数如下:

voidUARTConsoleInit(void){#if (0 == UART_STDIO_INSTANCE){PSCModuleControl(SOC_PSC_0_REGS,9, 0, PSC_MDCTL_NEXT_ENABLE);UARTPinMuxSetup(0, FALSE);} #elif (1 == UART_STDIO_INSTANCE){PSCModuleControl(SOC_PSC_1_REGS,12, 0, PSC_MDCTL_NEXT_ENABLE);UARTPinMuxSetup(1, FALSE);} #else{PSCModuleControl(SOC_PSC_1_REGS,13, 0, PSC_MDCTL_NEXT_ENABLE);UARTPinMuxSetup(2, FALSE);}#endif UARTStdioInitExpClk(BAUD_115200, UART_RX_TRIG_LEVEL_1);}


PSCModuleControl(SOC_PSC_1_REGS,13, 0, PSC_MDCTL_NEXT_ENABLE);函数对电源和睡眠控制器1Power and Sleep Controller 1 (PSC1))进行设置

(手册P23

(指南P171

PSCModuleControl函数如下:

intPSCModuleControl (unsignedint baseAdd, unsignedint moduleId,unsignedint powerDomain, unsignedint flags){volatileunsignedint timeout = 0xFFFFFF;int retVal = 0;unsignedint status = 0; HWREG(baseAdd + PSC_MDCTL(moduleId)) = (flags & PSC_MDCTL_NEXT); if (powerDomain == 0){HWREG(baseAdd + PSC_PTCMD) = PSC_PTCMD_GO0;}else{HWREG(baseAdd + PSC_PTCMD) = PSC_PTCMD_GO1;} if (powerDomain == 0){do {status = HWREG(baseAdd + PSC_PTSTAT) & PSC_PTSTAT_GOSTAT0;} while (status && timeout--);}else{do {status = HWREG(baseAdd + PSC_PTSTAT) & PSC_PTSTAT_GOSTAT1;} while (status && timeout--);} if (timeout != 0){timeout = 0xFFFFFF;status = flags & PSC_MDCTL_NEXT;do {timeout--;} while(timeout &&(HWREG(baseAdd + PSC_MDSTAT(moduleId)) & PSC_MDSTAT_STATE) != status);} if (timeout == 0){retVal = -1;} return retVal;}


UART2LPSC号(local PSC number)为13,对应的LPSC模块控制寄存器为MDCTL13.该函数设置MDCTL13寄存器(PSC1 Module Control n Register (modules 0-31) (MDCTLn))的NEXT字段为3,设置UART2下一状态为使能态,使能UART2模块。

(指南P163

(指南P186

然后该函数HWREG(baseAdd + PSC_PTCMD) = PSC_PTCMD_GO0;句设置PTCMD寄存器的GO[0]位为1UART2power domain0PD0,见上P163图),从而将UART2状态切换到使能状态。

(指南P171

(指南P176

函数段do {

status = HWREG(baseAdd + PSC_PTSTAT) & PSC_PTSTAT_GOSTAT0;

} while (status && timeout--);等待UART2模块状态切换完成,如果没有切换,则status0,程序继续向下运行。

(指南P171

(指南P177

函数段do {

timeout--;

} while(timeout &&

(HWREG(baseAdd + PSC_MDSTAT(moduleId)) & PSC_MDSTAT_STATE) != status);等待UART2模块当前状态与使能状态一致。

(指南P171

(指南P184

(指南P184

使能UART2模块之后,就要使能UART2模块的功能引脚了。UARTPinMuxSetup(2, FALSE);UART2txrx脚所在的芯片引脚的功能设置为UART2TX脚和RX脚,具体细节可看这篇博客:

http://blog.csdn.net/zengaliang/article/details/78313279

UARTStdioInitExpClk(BAUD_115200, UART_RX_TRIG_LEVEL_1);函数设置串口参数为8数据位,1停止位,无校验,并使能发送和接收fifo,接收fifo的触发水平为1,即接收fifo每收到1个字节,立刻产生一个Receiver data ready中断,通知CPU进行处理。关于UART初始化的这一部分,参考这篇博文:

http://blog.csdn.net/zengaliang/article/details/78313279

UARTStdioInitExpClk函数如下:

staticvoidUARTStdioInitExpClk(unsignedint baudRate, unsignedint rxTrigLevel){// 使能接收 / 发送UARTEnable(UART_CONSOLE_BASE); // 串口参数配置// 8位数据位 1位停止位无校验UARTConfigSetExpClk(UART_CONSOLE_BASE,SOC_UART_2_MODULE_FREQ,baudRate,UART_WORDL_8BITS,UART_OVER_SAMP_RATE_16);  // 使能接收 / 发送 FIFOUARTFIFOEnable(UART_CONSOLE_BASE); // 设置接收 FIFO 级别UARTFIFOLevelSet(UART_CONSOLE_BASE, rxTrigLevel); }UARTEnable(UART_CONSOLE_BASE);使能发送和接收,该函数如下:voidUARTEnable (unsignedint baseAdd){/* Enable the Tx, Rx and the free running mode of operation. */HWREG(baseAdd + UART_PWREMU_MGMT) = (UART_FREE_MODE | \UART_RX_RST_ENABLE | \UART_TX_RST_ENABLE);}


函数设置UART2PWREMU_MGMT寄存器的FREEUTSRTURRST3位,使能UARTfree运行模式,UART将会正常运行,并使能UARTtransmitterreceiver

(手册P23

(指南P1430

(指南P1447

到这里,终于完成串口终端的初始化了,下一步,

EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,

EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX,

EVT_QUEUE_NUM);语句申请串口EDMA3发送通道。EDMA3_0UART2的发送事件对应的DMA通道号为31TCC设为31,当UART2发生发送中断事件并完成DMA数据传输时,返回的TCC码为31,从而会将IPR寄存器的第31位置为1.又因为DRAE1在前面把所有的位都置1了,所以只要IER中的31位打开(置位),就会产生EDMA3_0_CC0_INT1中断(EVT#8)。

(手册P102

(手册P93

然后,根据前面的EDMA中断初始化函数,EDMA3_0_CC0_INT1中断事件被interrupt selector映射到了C674X_MASK_INT4中断,所以,当EDMA3_0_CC0_INT1中断发生时,会产生C674X_MASK_INT4中断,进而CPU会调用C674X_MASK_INT4中断的中断服务函数Edma3ComplHandlerIsrEdma3ComplHandlerIsr函数如下:

voidEdma3ComplHandlerIsr(void){volatileunsignedint pendingIrqs;volatileunsignedint isIPR = 0; unsignedint indexl;unsignedint Cnt = 0; indexl = 1; IntEventClear(SYS_INT_EDMA3_0_CC0_INT1); isIPR = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_IPR(1));if(isIPR){while((Cnt < EDMA3CC_COMPL_HANDLER_RETRY_COUNT)&& (indexl != 0u)){indexl = 0u;pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_IPR(1)); while(pendingIrqs){if((pendingIrqs & 1u) == TRUE){HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_ICR(1)) = (1u << indexl); (*cb_Fxn[indexl])(indexl, EDMA3_XFER_COMPLETE);}++indexl;pendingIrqs >>= 1u;}Cnt++;}}}


函数中,先是清除EF寄存器中对应event31的事件标志位,然后读取EDMA3_0_CC0IPR寄存器值到pendingIrqs变量,判断是否有中断标志位为0,如果非0,说明有中断发生。然后从最低位开始,查找所有为1的位,每找到1位为1的位,就往ICR寄存器中相应位写1,将IPR的该位清0,然后调用对应该位的回调函数,对于UART2发送中断,因为其IPR31位被置1,所以调用的回调函数为*cb_Fxn[31]*cb_Fxn[indexl]为回调函数指针数组,共32个回调函数指针,对应32DMA事件,因为在主函数中注册了cb_Fxn[31]指向的回调函数为callback函数,所以第31个(下标从0开始)函数指针cb_Fxn[31]指向的是callback函数,所以*cb_Fxn[31]将会调用callback函数。

(手册P105

Callback函数如下:

voidcallback(unsignedinttccNum, unsignedint status){UARTDMADisable(SOC_UART_2_REGS, (UART_RX_TRIG_LEVEL_1 | UART_FIFO_MODE)); flag = 1;}


UARTDMADisable函数如下:

voidUARTDMADisable (unsignedint baseAdd, unsignedint flags){/* Enabling the FIFO mode of operation.*/HWREG(baseAdd + UART_FCR) = (flags & (UART_FIFO_MODE | UART_RX_TRIG_LEVEL));}


callback函数中,先是将DMAMODE1位清0,关闭DMA运行模式,然后将flag变量置1,再返回调用callback函数的EDMA3中断服务程序Edma3ComplHandlerIsr对所有的pendingIrqs置位标志都调用相应的*cb_Fxn[indexl]函数,当清除后,下一次进入while((Cnt < EDMA3CC_COMPL_HANDLER_RETRY_COUNT)&& (indexl != 0u))循环的时候,再读IPR寄存器值到pendingIrqs,此时应该是0了,如果不是继续处理,直到是为止。

(指南P1430

(指南P1436

主函数继续往下,函数

UartTransmitData(EDMA3_CHA_UART2_TX,EDMA3_CHA_UART2_TX, enter, buffLength);利用DMA通道发送程序已定义好的buffer数组到UART2THR寄存器,从而通过UART2将数据发送到上位机。

UartTransmitData函数如下:

voidUartTransmitData(unsignedint tccNum, unsignedint chNum,volatilechar *buffer, unsignedint buffLength){EDMA3CCPaRAMEntry paramSet; // 配置参数 RAMparamSet.srcAddr = (unsignedint)buffer;// 接收缓存寄存器 / 发送保持寄存器地址paramSet.destAddr = SOC_UART_2_REGS + 0;paramSet.aCnt = MAX_ACNT;paramSet.bCnt = (unsignedshort)buffLength;paramSet.cCnt = MAX_CCNT; // 源索引自增系数 1 即一个字节paramSet.srcBIdx = (short)1u; // 目标索引自增系数paramSet.destBIdx = (short)0u; // 异步传输模式paramSet.srcCIdx = (short)0u;paramSet.destCIdx = (short)0u;paramSet.linkAddr = (unsignedshort)0xFFFFu;paramSet.bCntReload = (unsignedshort)0u;paramSet.opt = 0x00000000u;paramSet.opt |= (EDMA3CC_OPT_DAM );paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT); // 写参数 RAMEDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet); // 使能 EDMA3 通道EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);}


函数中配置了参数ram集(PaRAM set),源地址为要发送的数据的地址buffer,目的地址为UART2THRtransmitter holding Register),该寄存器为只写寄存器,在FIFO模式下是作为16字节fifo使用,THR地址与RBRReceiver Buffer Register)一致,但是RBR是只读寄存器,因而两者可以共用同一地址而不会起冲突。paramSet的二维索引destBIdx和三维索引destCIdx都设为0,这样DMA每传输完1array的数据至目标地址RBR的时候,目标地址不会改变,仍然为RBR的地址。paramSetlinkAddr设为0xFFFFu,则当前的paramSet用完(exhaust)之后,不再从新的地址复制参数到该paramSet了。BCNTRLD设为0,当BCNT减到0的时候,不再重载值到BCNT了。

(手册P187

(指南P499

(指南P502

paramSetopt项(entry),DAMdestination address mode)设为1,即常值寻址模式(Constant addressing (CONST) mode.),当传输一个array时,假如目标寻址(destination addressing)到达了fifo的尽头(reaching fifo width),就会又重新从fifo的开头寻址(wrap around)。Opt项的tccNum设为31,则当数据传输完成后,tc返回给cctcc code31TCINTEN位置为1,使能传输完成中断(transfer complete interrupt),当DMA数据传输完成后,IPR寄存器的相应位(由opt项的tccNum确定)会被置1

(指南P556

(指南P556

(指南P557

(指南P557

设置完参数ram集(paramSet)就可以写paramSet到参数ram了(Parameter RAMPaRAM),调用EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, &paramSet);

EDMA3SetPaRAM函数如下:

voidEDMA3SetPaRAM(unsignedint baseAdd,unsignedint chNum,EDMA3CCPaRAMEntry* newPaRAM){unsignedintPaRAMId = chNum; /* PaRAM mapped to channel Number */unsignedint i = 0;unsignedint *sr = (unsignedint *)newPaRAM;volatileunsignedint *ds; ds = (unsignedint *)(baseAdd + EDMA3CC_OPT(PaRAMId)); for(i=0; i < EDMA3CC_PARAM_ENTRY_FIELDS; i++){*ds = *sr;ds++;sr++;}}


PaRAM表划分为多个PaRAM集(PaRAM set),前nPaRAM对应到前nDMA通道。EDMA3SetPaRAMparam的第31paramSet(对应DMA通道31paramset)的每一项(entry32位,unsigned int)进行赋值设置。

(指南P501

(指南P501

(指南P564

写完参数ram,还要使能EDMA3通道了。

EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS,chNum, EDMA3_TRIG_MODE_EVENT); EDMA3EnableTransfer函数如下:

unsignedintEDMA3EnableTransfer(unsignedint baseAdd,unsignedint chNum,unsignedint trigMode){unsignedint retVal = FALSE;switch (trigMode){case EDMA3_TRIG_MODE_MANUAL :if (chNum < SOC_EDMA3_NUM_DMACH){EDMA3SetEvt(baseAdd, chNum);retVal = TRUE;}break; case EDMA3_TRIG_MODE_QDMA :if (chNum < SOC_EDMA3_NUM_QDMACH){EDMA3EnableQdmaEvt(baseAdd, chNum);retVal = TRUE;}break; case EDMA3_TRIG_MODE_EVENT :if (chNum < SOC_EDMA3_NUM_DMACH){/*clear SECR & EMCR to clean any previous NULL request */EDMA3ClrMissEvt(baseAdd, chNum); /* Set EESR to enable event */EDMA3EnableDmaEvt(baseAdd, chNum);retVal = TRUE;}break; default :retVal = FALSE;break;}return retVal;}


启动DMA传输的触发模式为事件触发,EDMA3ClrMissEvt(baseAdd, chNum);函数如下:

voidEDMA3ClrMissEvt(unsignedint baseAdd,unsignedint chNum){if(chNum < 32){/*clear SECR to clean any previous NULL request */HWREG(baseAdd + EDMA3CC_S_SECR(regionId)) = (0x01u << chNum);/*clear EMCR to clean any previous NULL request */HWREG(baseAdd + EDMA3CC_EMCR) |= (0x01u << chNum);}else{HWREG(baseAdd + EDMA3CC_S_SECRH(regionId)) = (0x01u << (chNum - 32));/*clear EMCRH to clean any previous NULL request */HWREG(baseAdd + EDMA3CC_EMCRH) |= (0x01u << (chNum - 32));}}


该函数往SECR寄存器的chNum位写1,清除SERchNum位,从而清除之前的无效(Null)的DMA传输请求。只有chNum位清0了,后续的在该通道(chNum通道)对应的事件发生时,EDMA3CC才会对该通道后续发生的事件进行处理(prioritize)。然后函数又清除了EMR寄存器中对应chNum的位。

(指南P505

(手册P105

(指南P590

 

 

(指南P590

(指南P562

(指南P571

EDMA3EnableDmaEvt函数如下:

voidEDMA3EnableDmaEvt(unsignedint baseAdd,unsignedint chNum){if(chNum < 32){/* (EESR) - set corresponding bit to enable DMA event */HWREG(baseAdd + EDMA3CC_S_EESR(regionId)) |= (0x01u << chNum);}else{/* (EESRH) - set corresponding bit to enable DMA event */HWREG(baseAdd + EDMA3CC_S_EESRH(regionId)) |= (0x01u << (chNum - 32));}}


该函数对EESR寄存器的chNum置位,从而对EER的相应位置位,使能chNum通道对应的事件。至此,就完成了EDMA3通道的使能工作,使能了#31DMA通道,当#31通道对应的事件(UART2 Transmit)发生时,#31DMA通道就会进行数据传输。

(手册P105

(指南P589

回到主函数,完成EDMA的设置工作后,还要使能串口的DMA模式,设置FCRFIFO Control Register)的DMAMODE1位和FIFOEN位,使能DMA模式和FIFO模式,并设置接收队列的触发级别(Receiver FIFO trigger level)为1字节。当UART处于DMA模式和FIFO模式下,如果transmitter FIFO为空时,UART就会发送UTXEVT(即UART2 Transmit事件,对应#31DMA通道)给EDMA控制器,从而启动DMA数据传输,将数据从定义好的buffer数组发送到UART2.

(指南P1429

发送完数据后,等待回调函数返回。只有当EDMA3中断事件发生时,才会调用回调函数,从而将flag标志置为1,否则,程序没有发生EDMA3中断事件,程序会一直停留在while(flag == 0);这句话这里。在回调函数callback里,因为DMA传输已经完成,所以要关闭UARTDMA模式。UART接收数据函数UartReceiveData和发送数据函数大同小异。只是接收数据函数中,paramSet的源地址变成了UART2RBR,目标地址变成了buffer数组。函数从UART2RBR读取接收到的数据,然后写到buffer数组里。还有,paramSetopt项的srcBIdx =0destBIdx=1SAMSource address mode)位置为1,因为这时FIFO已经变成了源,所以要让fifo读取指针循环,就要设置源寻址模式为Constant addressing (CONST) mode,其它的设置就与UART发送数据的情况是一样的了。

 

最后,不使用EDMA3通道的时候,还要把前面所申请的EDMA3通道(#31#30,对应UART2TX事件和RX事件)给释放了。EDMA3FreeChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,

EDMA3_CHA_UART2_TX, EDMA3_TRIG_MODE_EVENT,

EDMA3_CHA_UART2_TX, EVT_QUEUE_NUM);

函数如下:

unsignedintEDMA3FreeChannel(unsignedint baseAdd, unsignedint chType,unsignedint chNum, unsignedint trigMode,unsignedint tccNum, unsignedint evtQNum){unsignedint retVal = FALSE;if (chNum < SOC_EDMA3_NUM_DMACH){EDMA3DisableTransfer(baseAdd, chNum, trigMode);/* Disable the DMA channel in the shadow region specific register*/EDMA3DisableChInShadowReg(baseAdd, chType, chNum); EDMA3UnmapChToEvtQ( baseAdd, chType, chNum);if (EDMA3_CHANNEL_TYPE_DMA == chType){/* Interrupt channel nums are < 32 */if (tccNum < SOC_EDMA3_NUM_DMACH){/* Disable the DMA Event Interrupt */EDMA3DisableEvtIntr(baseAdd, chNum);retVal = TRUE;}}elseif (EDMA3_CHANNEL_TYPE_QDMA== chType){/* Interrupt channel nums are < 8 */if (tccNum < SOC_EDMA3_NUM_QDMACH){/* Disable the QDMA Event Interrupt */EDMA3DisableEvtIntr(baseAdd, chNum);retVal = TRUE;}}}return retVal;}


EDMA3DisableTransfer(baseAdd, chNum, trigMode);disablechNum通道的DMA传输,函数如下:

unsignedintEDMA3DisableTransfer(unsignedint baseAdd,unsignedint chNum,unsignedint trigMode){unsignedint retVal = FALSE;switch (trigMode){case EDMA3_TRIG_MODE_MANUAL :if (chNum < SOC_EDMA3_NUM_DMACH){EDMA3ClrEvt(baseAdd, chNum);retVal = TRUE;}break; case EDMA3_TRIG_MODE_QDMA :if (chNum < SOC_EDMA3_NUM_QDMACH){EDMA3DisableQdmaEvt(baseAdd, chNum);retVal = TRUE;}break; case EDMA3_TRIG_MODE_EVENT :if (chNum < SOC_EDMA3_NUM_DMACH){/*clear SECR & EMCR to clean any previous NULL request */EDMA3ClrMissEvt(baseAdd, chNum); /* Set EESR to enable event */EDMA3DisableDmaEvt(baseAdd, chNum);retVal = TRUE;}break; default :retVal = FALSE;break;}return retVal;}


该函数中,如果DMA传输触发模式是事件触发,则清除SECREMCR寄存器,从而清除missed event,然后往EESR的相应位写1,清除EER寄存器的相应位,禁用该DMA通道的事件触发中断,可参考上面的分析。回到EDMA3FreeChannel函数,

EDMA3DisableChInShadowReg函数如下:

voidEDMA3DisableChInShadowReg(unsignedint baseAdd,unsignedint chType,unsignedint chNum){/* Allocate the DMA/QDMA channel */if (EDMA3_CHANNEL_TYPE_DMA == chType){/* FOR TYPE EDMA*/if(chNum < 32){/* Enable the DMA channel in the DRAE registers */HWREG(baseAdd + EDMA3CC_DRAE(regionId)) &= ~(0x01u << chNum);}else{/* Enable the DMA channel in the DRAEH registers */HWREG(baseAdd + EDMA3CC_DRAEH(regionId)) &= ~(0x01u << (chNum - 32));}}elseif (EDMA3_CHANNEL_TYPE_QDMA== chType){/* FOR TYPE QDMA *//* Enable the QDMA channel in the DRAE/DRAEH registers */HWREG(baseAdd + EDMA3CC_QRAE(regionId)) &= ((~0x01u) << chNum);}}


该函数对DRAEm(本程序是DRAE1)的chNum#30#31)位清0,从而关闭第chNumDMA通道,通过影子区域mm=1)对任何的DMA通道寄存器的第chNum位的访问都是无效的。这样,就完成了关闭影子区域m对第chNumDMA通道的访问了。回到EDMA3FreeChannel函数。

(指南P577

函数继续调用EDMA3UnmapChToEvtQ( baseAdd, chType, chNum);EDMA3UnmapChToEvtQ函数如下:

void EDMA3UnmapChToEvtQ(unsignedint baseAdd,unsignedint chType,unsignedint chNum){if (EDMA3_CHANNEL_TYPE_DMA == chType){/* Unmap DMA Channel to Event Queue */HWREG(baseAdd + EDMA3CC_DMAQNUM((chNum) >> 3u)) |=EDMA3CC_DMAQNUM_CLR(chNum);}elseif (EDMA3_CHANNEL_TYPE_QDMA == chType){/* Unmap QDMA Channel to Event Queue */HWREG(baseAdd + EDMA3CC_QDMAQNUM) |=EDMA3CC_QDMAQNUM_CLR(chNum);}}


该函数将chNum通道所缓存的队列号恢复为默认(default)状态,默认为队列0。这里好像Ti的库函数有点问题,清队列号应该是&=而不是|=

(指南P568

(指南P568

最后再来看看Edma中断错误服务函数Edma3CCErrHandlerIsr,当发生EDMA3CC错误中断时,就会执行该函数。函数如下:

voidEdma3CCErrHandlerIsr(){volatileunsignedint pendingIrqs = 0;unsignedint regionNum = 0;unsignedint evtqueNum = 0;unsignedint index = 1;unsignedint Cnt = 0; IntEventClear(SYS_INT_EDMA3_0_CC0_ERRINT); if((HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMR) != 0 ) || \(HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMR) != 0) || \(HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERR) != 0)){while((Cnt < EDMA3CC_ERR_HANDLER_RETRY_COUNT) && (index != 0u)){index = 0u;pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMR);while(pendingIrqs){if((pendingIrqs & 1u) == TRUE){HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMCR) = (1u<<index);HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_SECR(regionNum)) = (1u<<index);} ++index;pendingIrqs >>= 1u;} index = 0u;pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMR); while(pendingIrqs){if((pendingIrqs & 1u)==TRUE){HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMCR) = (1u<<index);HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_QSECR(0)) = (1u<<index);} ++index;pendingIrqs >>= 1u;} index = 0u;pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERR);             if(pendingIrqs != 0u)            {                for(evtqueNum = 0u; evtqueNum < EDMA3_0_NUM_EVTQUE; evtqueNum++)                {                    if((pendingIrqs & (1u << evtqueNum)) != 0u)                    {                        HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERRCLR) = (1u << evtqueNum);                    }                 }                  if ((pendingIrqs & (1 << EDMA3CC_CCERR_TCCERR_SHIFT)) != 0u)                 {                     HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERRCLR) = \                         (0x01u << EDMA3CC_CCERR_TCCERR_SHIFT);                 }                  ++index;            }             Cnt++;}}}


函数先是清除ER寄存器中EDMA_CC0_ERRINT对应的位,然后读取EMRQEMRCCERR三个寄存器的值,如果非0,则有错误标志,需要对其清除。通过写EMCRSER清除EMR标志,及event missed错误。写QEMCRQSECR清除QDMA错误。写CCERRCLR清除CCERR的错误。

(指南P527

(指南P562

至此,整个程序就分析完了!

原创粉丝点击