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

int main(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;volatile char enter[] = "Tronlong UART2 EDMA3 Application......\n\rPlease Enter 20 bytes from keyboard\r\n";volatile char buffer[RX_BUFFER_SIZE];unsigned int 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();,函数如下:

void EDMA3InterruptInit(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号和5号CPU可屏蔽中断C674X_MASK_INT4和C674X_MASK_INT5的服务函数,分别为Edma3ComplHandlerIsr和Edma3CCErrHandlerIsr,然后将8号中断事件SYS_INT_EDMA3_0_CC0_INT1(EVT8)映射到C674X_MASK_INT4,将56号中断事件SYS_INT_EDMA3_0_CC0_ERRINT(EVT56)映射到C674X_MASK_INT5,并使能这两个CPU可屏蔽中断。SYS_INT_EDMA3_0_CC0_INT1为EDMA3_0模块的通道控制器0影子区域1传输完成的中断标志,6748有两个EDMA3模块,分别为EDMA3_0和EDMA3_1,每个EDMA_n模块都只有一个通道控制器CC0(channel controller 0)。

(手册P93~94)

(指南P493)

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

(指南P491)

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

(指南P518)

(指南P519)

(手册P104)

(手册P105)

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

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

(指南P524)

(指南P578)

(指南P577)

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

(mega手册P162)

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

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

EDMA3Init代码如下:

void EDMA3Init(unsignedint baseAdd,
unsigned int queNum)
{unsigned int count = 0;unsigned int i = 0;#ifdef _TMS320C6X/* For DSP, regionId is assigned here and used globally in the driver */regionId = (unsigned int)1u;#else/* FOR ARM, regionId is assigned here and used globally in the driver */regionId = (unsigned int)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,即使能影子区域1对EDMA3_0的CC0所有32个DMA通道的访问。当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 Register,DMAQNUM),将4个DMAQNUM寄存器都清为0,即所有通道的事件请求都放进队列0里。然后把QDMAQNUM寄存器也做同样的初始化操作,这样EDMA3初始化就完成了。

(指南P562)

(指南P568)

(指南P496)

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

void UARTStdioInit(void)
{UARTConsoleInit();
}

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

void UARTConsoleInit(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);}#endifUARTStdioInitExpClk(BAUD_115200, UART_RX_TRIG_LEVEL_1);
}

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

(手册P23)

(指南P171)

PSCModuleControl函数如下:

int PSCModuleControl (unsigned int baseAdd, unsigned int moduleId,
unsigned int powerDomain, unsigned int flags)
{volatile unsigned int timeout = 0xFFFFFF;int retVal = 0;unsigned int 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;
}

UART2的LPSC号(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]位为1,UART2的power domain为0(PD0,见上P163图),从而将UART2状态切换到使能状态。

(指南P171)

(指南P176)

函数段

do {status = HWREG(baseAdd + PSC_PTSTAT) & PSC_PTSTAT_GOSTAT0;} while (status && timeout--);

等待UART2模块状态切换完成,如果没有切换,则status为0,程序继续向下运行。

(指南P171)

(指南P177)

函数段

do {timeout--;} while(timeout &&(HWREG(baseAdd + PSC_MDSTAT(moduleId)) & PSC_MDSTAT_STATE) != status);

等待UART2模块当前状态与使能状态一致。

(指南P171)

(指南P184)

(指南P184)

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

C6748_UART中断_YangYuke的博客-CSDN博客

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

C6748_UART中断_YangYuke的博客-CSDN博客

UARTStdioInitExpClk函数如下:

static void UARTStdioInitExpClk(unsigned int baudRate, unsigned int 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);使能发送和接收,该函数如下:

void UARTEnable (unsigned int 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);
}

函数设置UART2的PWREMU_MGMT寄存器的FREE、UTSRT、URRST3位,使能UART的free运行模式,UART将会正常运行,并使能UART的transmitter和receiver。

(手册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_0的UART2的发送事件对应的DMA通道号为31,TCC设为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中断的中断服务函数Edma3ComplHandlerIsr。Edma3ComplHandlerIsr函数如下:

void Edma3ComplHandlerIsr(void)
{volatile unsigned int pendingIrqs;volatile unsigned int isIPR = 0;unsigned int indexl;unsigned int 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_CC0的IPR寄存器值到pendingIrqs变量,判断是否有中断标志位为0,如果非0,说明有中断发生。然后从最低位开始,查找所有为1的位,每找到1位为1的位,就往ICR寄存器中相应位写1,将IPR的该位清0,然后调用对应该位的回调函数,对于UART2发送中断,因为其IPR的31位被置1,所以调用的回调函数为*cb_Fxn[31],*cb_Fxn[indexl]为回调函数指针数组,共32个回调函数指针,对应32个DMA事件,因为在主函数中注册了cb_Fxn[31]指向的回调函数为callback函数,所以第31个(下标从0开始)函数指针cb_Fxn[31]指向的是callback函数,所以*cb_Fxn[31]将会调用callback函数。

(手册P105)

Callback函数如下:

void callback(unsigned int tccNum, unsigned int status)
{UARTDMADisable(SOC_UART_2_REGS, (UART_RX_TRIG_LEVEL_1 | UART_FIFO_MODE));flag = 1;
}

UARTDMADisable函数如下:

void UARTDMADisable (unsigned int baseAdd, unsigned int 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数组到UART2的THR寄存器,从而通过UART2将数据发送到上位机。

UartTransmitData函数如下:

void UartTransmitData(unsigned int tccNum, unsigned int chNum,
volatile char *buffer, unsigned int 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, paramSet);// 使能 EDMA3 通道EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
}

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

(手册P187)

(指南P499)

(指南P502)

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

(指南P556)

(指南P556)

(指南P557)

(指南P557)

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

EDMA3SetPaRAM函数如下:

void EDMA3SetPaRAM(unsigned int baseAdd,
unsigned int chNum,
EDMA3CCPaRAMEntry* newPaRAM)
{unsigned intPaRAMId = chNum; /* PaRAM mapped to channel Number */unsigned int i = 0;unsigned int *sr = (unsigned int *)newPaRAM;volatile unsigned int *ds;ds = (unsigned int *)(baseAdd + EDMA3CC_OPT(PaRAMId));for(i=0; i < EDMA3CC_PARAM_ENTRY_FIELDS; i++){*ds = *sr;ds++;sr++;}
}

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

(指南P501)

(指南P501)

(指南P564)

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

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

unsigned int EDMA3EnableTransfer(unsigned int baseAdd,
unsigned int chNum,
unsigned int trigMode)
{unsigned int 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);函数如下:

void EDMA3ClrMissEvt(unsigned int baseAdd,
unsigned int 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,清除SER的chNum位,从而清除之前的无效(Null)的DMA传输请求。只有chNum位清0了,后续的在该通道(chNum通道)对应的事件发生时,EDMA3CC才会对该通道后续发生的事件进行处理(prioritize)。然后函数又清除了EMR寄存器中对应chNum的位。

(指南P505)

(手册P105)

(指南P590)

(指南P590)

(指南P562)

(指南P571)

EDMA3EnableDmaEvt函数如下:

void EDMA3EnableDmaEvt(unsigned int baseAdd,
unsigned int 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模式,设置FCR(FIFO 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传输已经完成,所以要关闭UART的DMA模式。UART接收数据函数UartReceiveData和发送数据函数大同小异。只是接收数据函数中,paramSet的源地址变成了UART2的RBR,目标地址变成了buffer数组。函数从UART2的RBR读取接收到的数据,然后写到buffer数组里。还有,paramSet的opt项的srcBIdx =0,destBIdx=1,SAM(Source address mode)位置为1,因为这时FIFO已经变成了源,所以要让fifo读取指针循环,就要设置源寻址模式为Constant addressing (CONST) mode,其它的设置就与UART发送数据的情况是一样的了。
最后,不使用EDMA3通道的时候,还要把前面所申请的EDMA3通道(#31,#30,对应UART2的TX事件和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);

函数如下:

unsigned int EDMA3FreeChannel(unsigned int baseAdd, unsigned int chType,
unsigned int chNum, unsigned int trigMode,
unsigned int tccNum, unsigned int evtQNum)
{unsigned int 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);disable了chNum通道的DMA传输,函数如下:

unsigned int EDMA3DisableTransfer(unsignedint baseAdd,
unsigned int chNum,
unsigned int trigMode)
{unsigned int 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传输触发模式是事件触发,则清除SECR和EMCR寄存器,从而清除missed event,然后往EESR的相应位写1,清除EER寄存器的相应位,禁用该DMA通道的事件触发中断,可参考上面的分析。回到EDMA3FreeChannel函数,

EDMA3DisableChInShadowReg函数如下:

void EDMA3DisableChInShadowReg(unsigned int baseAdd,
unsigned int chType,
unsigned int 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));}}else if (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,从而关闭第chNum个DMA通道,通过影子区域m(m=1)对任何的DMA通道寄存器的第chNum位的访问都是无效的。这样,就完成了关闭影子区域m对第chNum个DMA通道的访问了。回到EDMA3FreeChannel函数。

(指南P577)

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

void EDMA3UnmapChToEvtQ(unsigned int baseAdd,
unsigned int chType,
unsigned int chNum)
{if (EDMA3_CHANNEL_TYPE_DMA == chType){/* Unmap DMA Channel to Event Queue */HWREG(baseAdd + EDMA3CC_DMAQNUM((chNum) >> 3u)) |=EDMA3CC_DMAQNUM_CLR(chNum);}else if (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错误中断时,就会执行该函数。函数如下:

void Edma3CCErrHandlerIsr()
{volatile unsigned int pendingIrqs = 0;unsigned int regionNum = 0;unsigned int evtqueNum = 0;unsigned int index = 1;unsigned int 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对应的位,然后读取EMR、QEMR、CCERR三个寄存器的值,如果非0,则有错误标志,需要对其清除。通过写EMCR,SER清除EMR标志,及event missed错误。写QEMCR和QSECR清除QDMA错误。写CCERRCLR清除CCERR的错误。

(指南P527)

(指南P562)

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

TMS320C6748_UART_EDMA相关推荐

最新文章

  1. 国内NLP竞赛平台一览(附平台连接)
  2. cocos2x (c++/lua) spine 文件的预加载
  3. 三层架构:软件设计架构
  4. android 蓝牙传输分包,彻底掌握Android多分包技术(一)
  5. html radio 默认图片替换_html,css_如何更改radio、checkbox选项框背景图?,html,css - phpStudy...
  6. RabbitMQ 入门:1. Message Broker(消息代理)
  7. scala语言+Spark学习一箩筐
  8. 计数排序(count sort)
  9. 常用的python内置方法
  10. 在IDEA中进行开发时,Maven的插件或依赖显示红线的问题
  11. 逆clarke变换_克拉克(CLARKE)及帕克(PARK)变换.pdf
  12. 测试3.0u盘速度软件,分享三个3.0 U盘的性能测试,另征集测试
  13. 汉语转拼音(带音调和多音字识别)
  14. 阿里云SFTP配置方法及故障排查
  15. 怎么查看台式计算机网络密码,台式电脑怎么查看wifi密码_台式机如何看wifi密码?-192路由网...
  16. 技术可行性与操作可行性的资料搜集与分析
  17. MySQL从删库到跑路(3):神奇的select
  18. Nand2Tetris 计算机结构
  19. 卷积神经网络超详细介绍
  20. ear的英语怎么念_高中英语快速记忆法有哪些?

热门文章

  1. 2022-04-30 Unity核心2——Sprite
  2. b站视频之求知讲堂Java视频-多维数组
  3. 付费测算系统完整版/完美对接支付结算
  4. Huffman树在编码中有着广泛的应用。在这里,我们只关心Huffman树的构造过程。
  5. APM飞控板增加继电器控制
  6. 自娱自乐写爬虫 世纪佳缘篇
  7. BigPatent文本摘要数据集
  8. 这个牛逼的国产低代码生成器!现在开源了
  9. qpython 3h_python 逆向某咖啡小程序接口
  10. Gradle –如果测试失败,如何继续构建