在实际应用中,循环缓冲区的使用十分广泛,如果DMA每次接收一定字节然后再在中断中写入自己定义的循环缓冲区,这样又失去了使用DMA的意义。所以硬件上的循环缓冲区的实现非常重要。大部分芯片支持DMA循环模式,而NXP的eDMA则可以通过Scatter/Gather
模式来实现硬件循环缓冲区。
1 TCD Pool初始化
硬件循环缓冲区的代码与上一节大体相同,只不过增加了一个tcdPool
,在前面的初始化完成后,无需调用UART_ReceiveEDMA
,而是执行下面的函数来启动硬件循环缓冲区:
__attribute__((aligned(ALIGN))) uint8_t g_ringBuffer[256];
__attribute__((section("DataQuickAccess"))) __attribute__((aligned(sizeof(edma_tcd_t)))) edma_tcd_t tcdMemoryPoolPtr[1];
/* Start ring buffer configuration. */
static void EXAMPLE_StartRingBufferEDMA(void)
{
edma_transfer_config_t xferConfig;
/* 添加tcdPool到edma结构体中 */
EDMA_InstallTCDMemory(&g_uartRxEdmaHandle, tcdMemoryPoolPtr, 1U);
/* 参考上一节博客的分析 */
EDMA_PrepareTransfer(&xferConfig, (void *)UART_GetDataRegisterAddress(UART0), sizeof(uint8_t), g_ringBuffer,
sizeof(uint8_t), sizeof(uint8_t), 256, kEDMA_PeripheralToMemory);
/* 开始传输:设置可以被加载到TCD内存中的TCD的数量 */
g_uartRxEdmaHandle.tcdUsed = 1U;
/* 表示下一个加载到tcd内存池的索引 */
g_uartRxEdmaHandle.tail = 0U;
/* 初始化自己声明的tcd内存池中的寄存器为0,但该函数会使能CSR.DREQ=1,表示主循环结束后关闭DMA请求 */
EDMA_TcdReset(&g_uartRxEdmaHandle.tcdPool[0U]);
/* 将在EDMA_PrepareTransfer中设置到的参数应用到tcdPool中
* 由于使能了Scatter/Gather模式,理论上不能在每次主循环结束后关闭DMA请求,故在此函数中清除DREQ位
*/
EDMA_TcdSetTransferConfig(&g_uartRxEdmaHandle.tcdPool[0U], &xferConfig, tcdMemoryPoolPtr);
/* 打开主循环完成中断,为了计算收到数据的字节数 */
g_uartRxEdmaHandle.tcdPool[0U].CSR |= DMA_CSR_INTMAJOR_MASK;
/* 将tcdPool中的TCD内容加载到DMA通道对应的TCD内存中 */
EDMA_InstallTCD(DMA0, UART_RX_DMA_CHANNEL, &g_uartRxEdmaHandle.tcdPool[0U]);
/* 开始eDMA传输 */
EDMA_StartTransfer(&g_uartRxEdmaHandle);
/* 使能串口DMA传输:UARTx->C2.RIE */
UART_EnableRxDMA(UART0, true);
}
(1)EDMA_InstallTCDMemory
上一节,我们分析了串口eDMA的