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.

[参考译文] AM6442:AM6442 (R5F + FreeRTOS):轻系统负载下的 SPI RX 外设中断可靠性问题

Guru**** 2812305 points

Other Parts Discussed in Thread: AM6442, AM263P4

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1598760/am6442-am6442-r5f-freertos-spi-rx-peripheral-interrupt-reliability-issues-under-light-system-load

器件型号: AM6442
主题: AM263P4 中讨论的其他器件

大家好!
我正在寻找有关如何做出选择的建议 SPI 外设接收更加可靠 亮起 AM6442 打开或关闭 Cortex-R5F(内核 0) 一方 FreeRTOS

受影响的平台

  • AM6442 、固件在上运行 R5FSS0 内核 0

  • 之前在上观察到的类似行为 AM263P4 、但要触发此问题要困难得多

问题描述

当系统主要处于空闲状态时、SPI 接收工作正常。 不过、一旦系统体验到偶数 轻负载额外 、SPI 接收变得不可靠、并且经常出现故障 FIFO 错误 (Rx 超限/TX 欠运转 — 我需要仔细检查哪个确切是显性的)。

“加载“并不意味着大量 CPU 使用—通常足以:

  • 运行一些其他 FreeRTOS 任务(即使非空闲总时间很低)、

  • 启用调试日志记录(CCS IDE 打印/日志记录)、

  • 或通常增加任务调度活动。

亮起 AM6442 、该问题甚至可能出现在~ 5%非空闲时间
亮起 AM263P4 、相同的固件需要明显更高的活动才能显示类似的症状。

SPI 配置/流量特性

  • SPI 时钟: 17MHz

  • 传输周期: 每一次 100ms (≈10 FPS)

  • 有效载荷大小: 固定的 2012 字节

  • 接口: 3 线 SPI(无专用 CS 引脚)

接收效果 中断驱动 。 我想保持这种方式(即没有轮询/前台阻塞)。

观察到的行为

  • SPI 数据已知存在于总线上。

  • 在负载条件下、SPI RX 中断看起来是 服务太晚或根本不提供服务 、导致 FIFO 错误。

  • 发生这种情况后、通信就会中断。

我尝试过的(没有成功)

1) 提高中断优先级

  • 尝试了多个 SPI 中断优先级。

  • 已确保优先级仍允许FromISR使用 FreeRTOS API。

  • 没有提高可靠性。

2) FreeRTOS 中断优先级屏蔽

  • 使用以下命令在启用优先级屏蔽的情况下重建 FreeRTOS:
    EN_MAX_SYSCALL_INTR_PRI_CRIT_SECTION

  • 测试了不同的中断优先级配置。

  • 没有观察到任何改善。

3) FIQ 中断

  • 将 SPI 外设中断设置为 FIQ 中断而不是 IRQ。

  • SPI 接收因该中断而完全中断。 驱动程序似乎根本没有收到任何数据。

我在问什么

我很欣赏有关的指导 如何在 AM6442 R5F 上使 SPI 外设接收稳健可靠 、具体来说:

  1. 在 FreeRTOS 下使用 SPI RX 中断时、这是否是 AM64x R5F 上的已知限制或常见问题?

  2. 是否有建议的 SPI FIFO 阈值、中断触发电平或驱动器设置 来提高对中断延迟的耐受性?

  3. DMA 为了确保可靠运行、即使在这样的中等数据速率下也是如此?

  4. CCS 调试打印/日志记录是否已知会引入足够的延迟或关键部分 时间敏感型 SPI RX?

  5. 是否有特定于 R5F 或特定于 VIM 的中断配置缺陷 这就解释了为什么不及时为 SPI ISR 提供服务?

我的目标是实现 近 100%可靠的 SPI 接收 不占用前台(中断驱动或基于 DMA 的解决方案没问题)。

如果需要、我可以提供:

  • SPI 驱动程序配置详细信息 (FIFO、阈值、驱动程序 API)、

  • 中断号和优先级、

  • 无论此参考设计是使用 TI MCU+ SDK SPI 驱动程序还是自定义实现。

提前感谢您提供任何见解或最佳实践建议。

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

    您好:

    接收是 中断驱动 。 我想保持这种方式(即没有轮询/前台阻塞)。

    该配置处于中断模式和回调模式。 纠正我的理解。

    SPI 驱动程序配置详细信息 (FIFO、阈值、驱动程序 API)、

    请与我分享 MCSPI 配置、您只需共享 example.syscfg 文件、我可以检索 SPI 的配置。

    这是使用 TI MCU+ SDK SPI 驱动程序还是自定义实现方案。

    请也分享此内容。

    此致、

    Vaibhav

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

    MCSPI syscfg:

    IRQ 处理程序:

    #define INTC_BASE_ADDR 0x2FFF0000u
    
    #define RADAR_SPI_INTR_NUM 204
    
    const MCSPILLD_Handle radarSpiMcspiHandle = &gMcspiObject[RADAR_SLAVE_SPI];
    const uint32_t radarSpiMcspiIntrNum = RADAR_SPI_INTR_NUM;
    const uint32_t radarSpiMcspiVimStsAddr = INTC_BASE_ADDR + (0x404U + (((RADAR_SPI_INTR_NUM) >> 5) & 0xFU) * 0x20U);
    const uint32_t radarSpiMcspiVimClrMask = 0x1U << ((RADAR_SPI_INTR_NUM) &0x1FU);
    
    
    __attribute__((__section__(".text.hwi"), noinline, naked, target("arm"), aligned(4))) void spi_hal_radar_isr(void)
    {
    ISR_CALL_LEVEL_NONFLOAT_REENTRANT(MCSPI_lld_peripheralIsr,
        radarSpiMcspiHandle,
        radarSpiMcspiIntrNum,
        radarSpiMcspiVimStsAddr,
        radarSpiMcspiVimClrMask,
        intcBaseAddr);
    }
    
    


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

    您好、Kacper、

    与 3 引脚模式类似、仅测试了仅 RX 模式。  

    请参阅 GitHub 存储库中的测试文件夹、这将帮助您了解已经存在的外设模式与其他组合(例如电线数量 (3/4),模式等)的现有和经过测试的组合。

    以下是该链接: https://github.com/TexasInstruments/mcupsdk-core/blob/next/test/drivers/mcspi/mcspi_controller_peripheral/test_mcspi_peripheral.c

    如果您发现一些有用的东西、请让我针织、否则我们可以深入了解驱动器、以提高相同的性能。

    此致、

    Vaibhav

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

    您好、

    在上一个有关 SPI RX 可靠性问题的主题上、我做了几个阐述
    根据建议进行的更改:

    所做的更改:

    1.切换到 DMA 模式作为一个实验,看看它是否比中断模式更可靠
    2.按照您的建议更改为仅 RX 模式 — 我认为两者之间的操作没有区别
    仅 RX 模式和 TX+RX 模式
    3.首先使用 TI 的 mcspi_loopback_DMA_LLD FreeRTOS 示例作为比较的参考
    我的实施

    初始测试:

    TI 示例运行良好、但它只执行<10 个传输、然后退出。 I
    将其修改为无限运行(在无限循环中的连续 SPI RX 传输)以进行匹配
    我的实际用例。

    问题描述:

    修改后、系统现在在的 ISR 轮询循环中挂起
    mcspi_DMA_UDMA.c:787:

    while ((effByteCnt != peerData) && (elapsedTicks < transaction->timeout))
    {
        retVal += Udma_getPeerData(rxChHandle, &peerData);
        elapsedTicks = hMcspiInit->clockP_get() - startTicks;
    }

    具体症状:

    - effByteCnt = 2012(正确的传输大小)
    - peerData =2316(始终比预期高+304 字节)
    -条件 effByteCnt != peerData 始终为真,导致无限轮询
    -超时从不会触发,因为 freeRTOS tick 中断在时耗尽
    在此 ISR 中旋转
    -有时系统被解开并继续一段时间,但随后再次挂起
    相同的环路
    -挂起通常发生在 10-20 次成功传输后

    我尝试过的方法:

    我尝试使用 FreeRTOS 程序、其中节拍中断具有最高优先级
    (希望 ISR 会正确超时)、但节拍中断仍然无法获得服务-
    轮询循环可防止其触发。

    问题:

    1.为什么 peerData 始终显示+304 字节? 这是 PEER4 中的已知开销吗
    在 AM64x 上注册? 该值在所有失败的传输中保持恒定。
    2.如何解决这个问题? 由于我使用的是 AM6442(不是 AM65x)、因此可以绕过 isCqRingMem 选项
    此轮询循环不可用。 ISR 上下文中的轮询循环似乎是不可避免的、
    但它假设 peerData 将与 effByteCnt 匹配。
    3.是否有+304 字节偏移预期行为? 您应该通过比较考虑这一点
    这是否表明存在配置问题?

    任何指导都将非常感谢!

    硬件:AM6442 SK-EVM
    SDK:MCU+ SDK 11.00.00.15
    模式:SPI 外设(从器件)、仅 RX、具有 UDMA 的 DMA
    传输大小:2012 字节

    我运行的代码:

    #include <kernel/dpl/DebugP.h>
    #include <kernel/dpl/HwiP.h>
    #include <kernel/nortos/dpl/r5/HwiP_armv7r_vim.h>
    
    #include "osal/eventgroup.h"
    
    #include "ti_board_open_close.h"
    #include "ti_drivers_config.h"
    #include "ti_drivers_open_close.h"
    
    /* MCSPI Channel Macro */
    #define APP_MCSPI_MSGSIZE            (2012U)
    #define APP_MCSPI_TRANSFER_LOOPCOUNT (10U)
    
    /* Event bits for SPI transfer synchronization */
    #define SPI_EVENT_TRANSFER_DONE  0x01U
    #define SPI_EVENT_TRANSFER_ERROR 0x02U
    
    static Osal_Eventgroup_t gSpiEvents;
    
    uint8_t gMcspiTxBuffer[APP_MCSPI_TRANSFER_LOOPCOUNT][APP_MCSPI_MSGSIZE]
        __attribute__((aligned(CacheP_CACHELINE_ALIGNMENT)));
    uint8_t gMcspiRxBuffer[APP_MCSPI_TRANSFER_LOOPCOUNT][APP_MCSPI_MSGSIZE]
        __attribute__((aligned(CacheP_CACHELINE_ALIGNMENT)));
    
    void* mcspi_dma_lld_main(void* args)
    {
        int32_t status;
        uint32_t count;
        uint32_t timeout = MCSPI_WAIT_FOREVER;
        MCSPI_ExtendedParams extendedParams;
        uint32_t frameCount = 0;
    
        DebugP_log("[MCSPI] Example started ...\r\n");
    
        /* Initialize event group for transfer synchronization */
        Eventgroup_Result evt_result = Eventgroup_init(&gSpiEvents);
        if (EVENTGROUP_OK != evt_result || gSpiEvents.eventgroupHandle == 0)
        {
            DebugP_log("ERROR: Failed to create event group\r\n");
            return NULL;
        }
    
        /* populate extended parameters */
        extendedParams.channel = gRadarSlaveSpiChCfg[0].chNum;
        extendedParams.csDisable = TRUE;
        extendedParams.dataSize = 8;
        count = APP_MCSPI_MSGSIZE / (extendedParams.dataSize / 8);
    
        /* Run infinite loop with circular buffer */
        while (1)
        {
            uint32_t bufIndex = frameCount % APP_MCSPI_TRANSFER_LOOPCOUNT;
    
            /* Clear event bits before starting transfer */
            Eventgroup_clearBits(&gSpiEvents, SPI_EVENT_TRANSFER_DONE | SPI_EVENT_TRANSFER_ERROR);
    
            /* Writeback buffer */
            CacheP_wb(gMcspiTxBuffer[bufIndex], sizeof(gMcspiTxBuffer[0]), CacheP_TYPE_ALLD);
            CacheP_wb(gMcspiRxBuffer[bufIndex], sizeof(gMcspiRxBuffer[0]), CacheP_TYPE_ALLD);
    
            status = MCSPI_lld_readWriteDma(gMcspiHandle[RADAR_SLAVE_SPI],
                                            gMcspiTxBuffer[bufIndex],
                                            gMcspiRxBuffer[bufIndex],
                                            count,
                                            timeout,
                                            &extendedParams);
    
            if (status != MCSPI_STATUS_SUCCESS)
            {
                DebugP_log("ERROR: Transfer initiation failed with status %d\r\n", status);
                continue;
            }
    
            /* Wait for transfer completion with timeout */
            uint32_t event = 0;
            bool const doNotClearBitsOnExit = false;
            bool const doWaitForAnyBit = false;
            static const uint32_t timeout_ms = 1000; /* 1 second timeout */
    
            evt_result = Eventgroup_waitForBits(&gSpiEvents,
                                                SPI_EVENT_TRANSFER_DONE | SPI_EVENT_TRANSFER_ERROR,
                                                doNotClearBitsOnExit,
                                                doWaitForAnyBit,
                                                timeout_ms,
                                                &event);
    
            /* Invalidate cache after DMA completes */
            CacheP_inv(gMcspiTxBuffer[bufIndex], sizeof(gMcspiTxBuffer[0]), CacheP_TYPE_ALLD);
            CacheP_inv(gMcspiRxBuffer[bufIndex], sizeof(gMcspiRxBuffer[0]), CacheP_TYPE_ALLD);
    
            if (evt_result != EVENTGROUP_OK)
            {
                DebugP_log("ERROR: Transfer timeout\r\n");
                continue;
            }
    
            if (event & SPI_EVENT_TRANSFER_ERROR)
            {
                DebugP_log("ERROR: Transfer error detected\r\n");
                continue;
            }
    
            /* Increment frame count and log on buffer wrap-around */
            frameCount++;
            if ((frameCount % 10) == 0)
            {
                DebugP_log("%u\r\n", frameCount);
            }
        }
    
        return NULL;
    }
    
    void radarSpiTransferCallback(void* args, uint32_t transferStatus)
    {
        if (transferStatus == MCSPI_TRANSFER_COMPLETED)
        {
            Eventgroup_setBitsFromISR(&gSpiEvents, SPI_EVENT_TRANSFER_DONE);
        }
        else
        {
            Eventgroup_setBitsFromISR(&gSpiEvents, SPI_EVENT_TRANSFER_ERROR);
        }
    }
    
    void radarSpiErrorCallback(void* args)
    {
        Eventgroup_setBitsFromISR(&gSpiEvents, SPI_EVENT_TRANSFER_ERROR);
    }
    
    

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

    您好:

    快速回顾一下、如果您希望所有传输仅在 1 片选下发生、并且不希望芯片选择在多个 MCSPI 传输之间置为无效、则可以设置:

     extendedParams.csDisable = true;

    最终目的

     extendedParams.csDisable = false;

    我还建议您测试您构建的相同示例、但使用的是本月中旬(即 12 月)发布的最新 SDK 11.2。

    它有很多修复、从 FIFO 问题到超时实现。

    以下是指向最新 SDK 的链接: www.ti.com/.../11.02.00.24

    此致、

    Vaibhav

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

    你好、Vaibhav、

    是的、我将两者都`为`Ω csDisable = true`和`Ω csDisable = false 、但这没有什么区别。

    同样、对于最新的 SDK、问题仍然是相同的。

    您能帮助我更深入地探讨一下这个主题吗? 我真的需要解决这个问题。

    此致、

    Kacper

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

    尊敬的 Kacper:

    TI 示例运行良好、但它只执行<10 次传输、然后退出。 I
    将其修改为无限运行(在无限循环中的连续 SPI RX 传输)以进行匹配
    我的实际用例。

    在传输 2012 字节少于 10 次时、它正常工作、但在无限循环中运行时、它卡在以下行:  

    https://github.com/TexasInstruments/mcupsdk-core/blob/next/source/drivers/mcspi/v0/lld/dma/udma/mcspi_dma_udma.c#L787

    请纠正我的理解。

    您还能告诉我、这种行为在第 n 次传输中是否始终可见、或者在任何传输中是否随机出现?

    期待您的答复。

    同时、我将尝试加入相同的示例、并以类似的方式接收一些字节。

    此致、

    Vaibhav