通过EDMA来实现UART的收发,可以减轻CPU的负担。主函数如下:
int main(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;
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);
}
#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函数如下:
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);
// 使能接收 / 发送 FIFO
UARTFIFOEnable(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;
// 配置参数 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, 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, ¶mSet);
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)
至此,整个程序就分析完了!