DSP

C6748_UART_EDMA

2019-07-13 17:25发布

通过EDMA来实现UART的收发,可以减轻CPU的负担。主函数如下: intmain(void) { // 外设使能配置 PSCInit(); // DSP 中断初始化 InterruptInit(); // EDMA3 中断初始化 EDMA3InterruptInit(); // EDMA3 初始化 EDMA3UARTInit(); // 初始化串口终端使用串口2 UARTStdioInit(); // 申请串口 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...... Please Enter 20 bytes from keyboard "; 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 GuideUsing 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(); }

该函数在demoStarterWareSourceStarterWareUtils路径下工程文件里的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); // 使能接收 / 发送 FIFO UARTFIFOEnable(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; // 配置参数 RAM paramSet.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); // 写参数 RAM EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet); // 使能 EDMA3 通道 EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT); }

函数中配置了参数ram