You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tiva C平台uDMA乒乓模式多缓冲区发送切换实现问询

Tiva平台uDMA+EPI多缓冲区发送优化方案

我正在Tiva平台上尝试用uDMA通过EPI接口发送多个1k大小的缓冲区,目前已经实现了两个缓冲区的乒乓模式发送,但不知道该怎么修改才能支持3个以上的缓冲区。现有相关代码片段如下:

现有缓冲区初始化代码

pa = send_data;
pb  = send_data2;
pc = send_data3;

void EPIIntHandler(void)
{
    uint32_t intStatus;

    //
    // Handle any EPI error IRQ signals.
    //
    intStatus = EPIIntErrorStatus(EPI0_BASE);
    if(intStatus)
    {
        g_ui32EPIErrors++;
    }
    EPIIntErrorClear(EPI0_BASE, intStatus);
    intStatus = EPIIntStatus(EPI0_BASE, false);

    if(intStatus)
    {
        g_ui32EPIStatus = intStatus;
    }

    //
    // Get any DMA related information. Clear the IRQ.
    //
    intStatus = uDMAIntStatus();
    uDMAIntClear(intStatus);

    if(( uDMAChannelModeGet(UDMA_CHANNEL_TMR1B | UDMA_PRI_SELECT) == UDMA_MODE_STOP) &&
            (pui32BufferStatus[0] == FILLING))
    {
        pui32BufferStatus[0] = FULL;
        pui32BufferStatus[1] = FILLING;

    }
    else if ((uDMAChannelModeGet(UDMA_CHANNEL_TMR1B | UDMA_ALT_SELECT) == UDMA_MODE_STOP) &&
            (pui32BufferStatus[1] == FILLING))
    {

        pui32BufferStatus[0] = FILLING;
        pui32BufferStatus[1] = FULL;
    }

}

现有主循环代码

if(pui32BufferStatus[0] == FULL)
{
    pui32BufferStatus[0] = EMPTY;
    MAP_uDMAChannelTransferSet(UDMA_CH21_EPI0TX | UDMA_PRI_SELECT,
                               UDMA_MODE_PINGPONG,
                               (void *)pa,
                               (void *)g_pusEPIFPGA,
                               SIZE_TX);


    MAP_EPIDMATxCount(EPI0_BASE, SIZE_TX);
    SwapBuffers(&pb, &pc);
}
//
// Check if the second buffer is full, if so process data.
//
if(pui32BufferStatus[1] == FULL)
{
    //
    // Indicate the Buffer data as been processed so new data can
    // be stored.

    pui32BufferStatus[1] = EMPTY;
    //
    MAP_uDMAChannelTransferSet(UDMA_CH21_EPI0TX | UDMA_ALT_SELECT,
                               UDMA_MODE_PINGPONG,
                               (void *)pb,
                               (void *)g_pusEPIFPGA,
                               SIZE_TX);


    SwapBuffers(&pa, &pb);



}

多缓冲区支持解决方案

要支持3个及以上缓冲区,不能再依赖固定的乒乓缓冲区指针,改用环形缓冲区队列的思路管理所有缓冲区,让uDMA乒乓通道持续从队列中取待发数据,中断仅标记传输完成状态,主循环负责填充新数据和更新队列。具体修改步骤如下:

1. 重构缓冲区管理结构

定义缓冲区数组、状态数组,用队列头尾指针管理待发缓冲区,替代原来的单个指针:

#define BUFFER_NUM 4  // 根据需求修改缓冲区数量
uint8_t send_data[BUFFER_NUM][1024];  // 每个缓冲区1k大小
volatile uint8_t pui32BufferStatus[BUFFER_NUM];  // 状态:EMPTY/FILLING/FULL
uint32_t queue_head = 0;  // 下一个要发送的缓冲区索引
uint32_t queue_tail = 0;  // 下一个可填充数据的缓冲区索引
uint32_t pri_buf_idx = 0; // 主DMA通道绑定的缓冲区索引
uint32_t alt_buf_idx = 1; // 备用DMA通道绑定的缓冲区索引

2. 简化中断处理函数

中断仅判断DMA通道传输完成,标记对应缓冲区状态为FULL,不再处理状态切换:

void EPIIntHandler(void)
{
    uint32_t intStatus;

    // 处理EPI错误中断
    intStatus = EPIIntErrorStatus(EPI0_BASE);
    if(intStatus)
    {
        g_ui32EPIErrors++;
    }
    EPIIntErrorClear(EPI0_BASE, intStatus);

    // 处理uDMA中断
    intStatus = uDMAIntStatus();
    uDMAIntClear(intStatus);

    // 检查主通道传输完成
    if(uDMAChannelModeGet(UDMA_CH21_EPI0TX | UDMA_PRI_SELECT) == UDMA_MODE_STOP)
    {
        pui32BufferStatus[pri_buf_idx] = FULL;
    }
    // 检查备用通道传输完成
    if(uDMAChannelModeGet(UDMA_CH21_EPI0TX | UDMA_ALT_SELECT) == UDMA_MODE_STOP)
    {
        pui32BufferStatus[alt_buf_idx] = FULL;
    }
}

3. 重写主循环逻辑

主循环负责检测DMA通道空闲,从队列中取已填充好的缓冲区配置到DMA,同时更新队列并填充新数据:

while(1)
{
    // 处理主DMA通道空闲情况
    if((uDMAChannelModeGet(UDMA_CH21_EPI0TX | UDMA_PRI_SELECT) == UDMA_MODE_STOP) && 
       (pui32BufferStatus[pri_buf_idx] == FULL))
    {
        pui32BufferStatus[pri_buf_idx] = EMPTY;
        // 配置主通道发送当前缓冲区
        MAP_uDMAChannelTransferSet(UDMA_CH21_EPI0TX | UDMA_PRI_SELECT,
                                   UDMA_MODE_PINGPONG,
                                   (void*)send_data[pri_buf_idx],
                                   (void*)g_pusEPIFPGA,
                                   SIZE_TX);
        MAP_EPIDMATxCount(EPI0_BASE, SIZE_TX);

        // 更新队列头,切换到下一个缓冲区
        queue_head = (queue_head + 1) % BUFFER_NUM;
        pri_buf_idx = queue_head;
        // 标记新缓冲区为填充状态,执行数据填充
        pui32BufferStatus[pri_buf_idx] = FILLING;
        FillBuffer(send_data[pri_buf_idx]); // 自定义数据填充函数
    }

    // 处理备用DMA通道空闲情况
    if((uDMAChannelModeGet(UDMA_CH21_EPI0TX | UDMA_ALT_SELECT) == UDMA_MODE_STOP) && 
       (pui32BufferStatus[alt_buf_idx] == FULL))
    {
        pui32BufferStatus[alt_buf_idx] = EMPTY;
        // 配置备用通道发送当前缓冲区
        MAP_uDMAChannelTransferSet(UDMA_CH21_EPI0TX | UDMA_ALT_SELECT,
                                   UDMA_MODE_PINGPONG,
                                   (void*)send_data[alt_buf_idx],
                                   (void*)g_pusEPIFPGA,
                                   SIZE_TX);
        MAP_EPIDMATxCount(EPI0_BASE, SIZE_TX);

        // 更新队列尾,切换到下一个缓冲区
        queue_tail = (queue_tail + 1) % BUFFER_NUM;
        alt_buf_idx = queue_tail;
        // 标记新缓冲区为填充状态,执行数据填充
        pui32BufferStatus[alt_buf_idx] = FILLING;
        FillBuffer(send_data[alt_buf_idx]); // 自定义数据填充函数
    }
}

4. 调整初始化逻辑

初始化时填充前两个缓冲区,标记为可发送状态:

void BufferInit(void)
{
    // 初始化所有缓冲区为空闲状态
    for(int i = 0; i < BUFFER_NUM; i++)
    {
        pui32BufferStatus[i] = EMPTY;
    }
    // 填充前两个缓冲区,供乒乓通道初始使用
    FillBuffer(send_data[0]);
    pui32BufferStatus[0] = FULL;
    FillBuffer(send_data[1]);
    pui32BufferStatus[1] = FULL;

    queue_head = 0;
    queue_tail = 1;
    pri_buf_idx = 0;
    alt_buf_idx = 1;
}

内容的提问来源于stack exchange,提问作者andre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 10:06:16