This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] UART-AM263X:MCU-PLUS-SDK RX–在重新布防可变长度接收时 64 字节后数据损坏

Guru**** 2774995 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1619090/mcu-plus-sdk-am263x-am263x-uart-dma-rx-data-corruption-after-64-bytes-when-re-arming-variable-length-reception

尊敬的团队:

我正在努力实现 AM263x 消息流 MCU+ SDK 版本 MCU_PLUS_SDK_am263x_09_02_00_55

我在中使用的是 UART 启用了 DMA 的回调模式

 

要求

我的接收长度是动态的:

  • 首先、I 接收 4 个字节(标头)。

  • 第 2 个字节和第 3 个字节决定有效载荷长度。

  • 在此基础上,我更新UART_Transaction.count.

  • 然后我UART_read()再次调用以接收剩余的有效负载。

  • 有效载荷接收完成后、我重新启动下一个 4 字节标头的接收。

来自外部器件的传入数据流是连续的。

发现问题

接收缓冲区将正确填充至 64 字节

64 字节后:

  • 第 65 个字节之后出现损坏。

  • 数据看起来像是从索引 0 重新启动或被覆盖。

  • 行为表明可能发生 FIFO 溢出(UART FIFO 大小看起来为 64 字节)。

要临时恢复、当 LSR 指示数据就绪时、我通过读取 RHR 寄存器来手动刷新 UART FIFO、但我不确定这是否是将 UART 驱动程序与 DMA 搭配使用的正确方法。



这是我的代码

static uint8_t gRadioComReceivebuffer[UART_COM_BUFSIZE]
    __attribute__((aligned(CacheP_CACHELINE_ALIGNMENT))) = {0U};

static uint8_t gRadioCOMCopyDMA[1024] = {0U};

#define UART_COM_BUFSIZE (1024)

static void SL_SetRadioCOMByteCount(uint16_t bCount)
{
    RMC_RxTrans.buf   = gRadioComReceivebuffer;
    RMC_RxTrans.count = bCount;
}

static void RADIO_MODEM_RX_CALLBACK(UART_Handle handle,
                                    UART_Transaction *trans)
{
    printf("received count = %d\n", trans->count);
    RMCRxBuf.rxCompleteFlag = 1U;
}

处理功能:

static void CopyReceiveData(void)
{
    if (RMCRxBuf.rxCompleteFlag == 1U)
    {
        RMCRxBuf.rxCompleteFlag = 0U;

        CacheP_inv((void *)gRadioComReceivebuffer,
                   RadioRXDMALen,
                   CacheP_TYPE_ALL);

        if (RadioRXDMALen == 4U)
        {
            for(uint8_t i = 0; i < 4; i++)
            {
                gRadioCOMCopyDMA[i] = gRadioComReceivebuffer[i];
            }

            RadioRXDMALen =
                ((gRadioCOMCopyDMA[2] << 8) |
                 (gRadioCOMCopyDMA[3])) + 5U;

            SL_SetRadioCOMByteCount(RadioRXDMALen);

            UART_read(gUartHandle[RADIO_COMM_PORT], &RMC_RxTrans);
        }
        else
        {
            for(uint16_t i = 0; i < RadioRXDMALen; i++)
            {
                gRadioCOMCopyDMA[i + 4U] =
                    gRadioComReceivebuffer[i];
            }

            RadioRXDMALen = 4U;
            SL_SetRadioCOMByteCount(RadioRXDMALen);

            UART_read(gUartHandle[RADIO_COMM_PORT], &RMC_RxTrans);

            VadateDMAmodel1(radioCOMRxByteCount);
            radioCOMRxByteCount = 0U;
        }
    }
}

uart configuration:
            .baudRate           = 57600,
            .dataLength         = UART_LEN_8,
            .stopBits           = UART_STOPBITS_1,
            .parityType         = UART_PARITY_NONE,
            .readMode           = UART_TRANSFER_MODE_CALLBACK,
            .readReturnMode     = UART_READ_RETURN_MODE_PARTIAL,
            .writeMode          = UART_TRANSFER_MODE_CALLBACK,
            .readCallbackFxn    = RADIO_MODEM_RX_CALLBACK,
            .writeCallbackFxn   = RADIO_MODEM_TX_CALLBACK,
            .hwFlowControl      = FALSE,//TRUE,
            .hwFlowControlThr   = UART_RXTRIGLVL_16,
            .transferMode       = UART_CONFIG_MODE_DMA,
            .skipIntrReg         = FALSE,
            .uartDmaIndex = 1,
            .intrNum            = 39U,
            .intrPriority       = 8U,
            .operMode           = UART_OPER_MODE_16X,
            .rxTrigLvl          = UART_RXTRIGLVL_1,
            .txTrigLvl          = UART_TXTRIGLVL_1,
            .rxEvtNum           = 0U,
            .txEvtNum           = 0U
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、

    感谢您的查询。 请注意、我们将无法为您完全调试定制应用。 我们一定可以提供有关调试的指针。

    SDK 文档将您的情形明确描述为一个已知问题:

    “在 UART DMA 模式下、事务完成后、如果远程端继续发送数据、则将在 FIFO 中接收该数据。 如果 FIFO 已满、则会发生 FIFO 溢出错误。 这是因为 DMA 在事务结束时被禁用、并且不再有数据从 FIFO 传输到主存储器。“ [3]

    对于连续数据流、在一个 DMA 事务完成和下一个事务开始之间有一个关键窗口。 在此期间、传入数据在 FIFO 中累积、如果超过 64 个字节、则会发生溢出[4]。

    参考资料:

    1. UART DMA 已知问题 — AM263x MCU+ SDK 用户指南
    2. UART DMA 连续数据接收 — AM263x SDK 用户指南
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    事务计数必须与触发级别匹配

    使用 DMA 时的附加约束是UART_Transaction.count必须是所配置接收触发电平的倍数 (rxTrigLvl):

    UART_Transaction.count 是 DMA 传输大小的倍数、这是由 UART_Attrs.rxTrigLvl 参数配置的、这一点很重要。

    如果您的动态有效载荷长度与此要求不一致、则 DMA 无法正确传输剩余字节、这会导致数据损坏。UART 事务计数要求 — AM263x SDK 用户指南

    建议解决方案

    方法 1:队列下次从回调读取(推荐)

    为防止连续数据流发生 FIFO 溢出、请直接从读取回调函数对下一个读取请求排队:

    void uartReadCallback(UART_Handle handle, UART_Transaction *transaction)
    {
        if (transaction->status == UART_TRANSFER_STATUS_SUCCESS)
        {
            if (receivingHeader)
            {
                // Process header to determine payload length
                uint16_t payloadLength = (rxBuffer[1] << 8) | rxBuffer[2];
                
                // Prepare NEW transaction structure for payload
                UART_Transaction payloadTrans;
                UART_Transaction_init(&payloadTrans);
                payloadTrans.buf = &rxBuffer[4];  // Start after header
                payloadTrans.count = payloadLength;
                payloadTrans.timeout = UART_WAIT_FOREVER;
                
                receivingHeader = false;
                
                // Queue payload read immediately from callback
                UART_read(handle, &payloadTrans);
            }
            else
            {
                // Payload received, process data
                processPayload();
                
                // Prepare NEW transaction structure for next header
                UART_Transaction headerTrans;
                UART_Transaction_init(&headerTrans);
                headerTrans.buf = rxBuffer;
                headerTrans.count = 4;
                headerTrans.timeout = UART_WAIT_FOREVER;
                
                receivingHeader = true;
                
                // Queue next header read immediately from callback
                UART_read(handle, &headerTrans);
            }
        }
    }

    该方法可确保 DMA 始终做好接收数据的准备、从而防止 FIFO 溢出[6]。

    方法 2:使用 UART_readCancel() 清空 FIFO

    如果您不能重构代码以将读数从回调中排队,请使用适当的 API 来刷新 FIFO.应用程序可以调用 uart_readCancel () 来终止事务并刷新 FIFO 以避免这种情况

    UART DMA 已知问题 — AM263x MCU+ SDK 用户指南

    // After processing header, before starting payload read
    UART_readCancel(uartHandle);  // Flush any accumulated data in FIFO
    
    // Now start payload read with new transaction
    UART_Transaction payloadTrans;
    UART_Transaction_init(&payloadTrans);
    payloadTrans.buf = payloadBuffer;
    payloadTrans.count = payloadLength;
    UART_read(uartHandle, &payloadTrans);

    方法 3:对未知长度使用部分读取模式

    对于您不知道确切字节计数的真正动态长度,请考虑使用UART_READ_RETURN_MODE_PARTIAL:

    “UART 驱动程序支持 UART_READ_RETURN_MODE_PARTIAL、每当 UART 外设上发生读取超时错误时、该功能就会取消阻止或执行回调。 如果读取 FIFO 不为空并且在特定数量的时钟周期内没有接收到新数据、则会发生读取超时。 当要读取的确切字节数未知时、可以使用此模式。“ [7]

    为什么手动 FIFO 冲洗不正确

    您当前手动读取 RHR 寄存器的方法是 不推荐 将高级 UART 驱动程序与 DMA 配合使用时:

    UART_getCharFifo()读取 RHR 寄存器[8]的低级 API 绕过驱动程序的状态管理。 在回调模式下将 UART 驱动程序与 DMA 一起使用时、手动读取 RHR 寄存器会干扰 DMA 控制器的运行和驱动程序的内部缓冲区管理、从而导致同步问题和数据损坏[9]。

    实施检查清单

    1. 从不修改 UART_TransactionUART_read()呼叫完成和回调完成之间构建成员
    2. 始终单独使用 UART_Transaction用于读取标头和有效载荷的结构
    3. 对下一次读取排队 缓冲区、以尽可能缩短 FIFO 累积时间
    4. 确保 UART_Transaction.countrxTrigLvl使用 DMA 时的倍数
    5. 应用 UART_readCancel()如果需要刷新 FIFO、则为 API(而非手动 RHR 读取)
    6. 请考虑 UART_READ_RETURN_MODE_PARTIAL协议的支持函数

    其他注意事项

    rxTrigLvl在中验证您的配置UART_Attrs。 对于连续的高速数据流、较低的触发电平(例如 1 或 8 个字节)可能有助于减少 FIFO 累积、但这会增加中断/DMA 开销

    参考资料:

    1. UART FIFO 大小定义 — AM263x SDK API 指南
    2. UART_read () API 文档 — AM263x SDK
    3. UART DMA 已知问题 — AM263x MCU+ SDK 用户指南
    4. UART DMA 连续数据接收 — AM263x SDK 用户指南
    5. UART 事务计数要求 — AM263x SDK 用户指南
    6. UART 回调模式操作 — AM263x SDK API 指南
    7. UART 部分读取模式 — AM263x SDK API 指南
    8. uart_getCharFifo() 低级 API - AM263x SDK
    9. UART DMA 手动触发示例 — AM263x MCU+ SDK 用户指南