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
相关产品推荐
相关产品推荐

