通过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号和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();代码如下:
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,即使能影子区域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();语句函数如下:
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);函数对电源和睡眠控制器1(Power
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;
}
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脚,具体细节可看这篇博客:
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);
}
函数设置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函数如下:
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_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函数如下:
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数组到UART2的THR寄存器,从而通过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