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.

[参考译文] MSPM0G3507:UART 中断无法以特定的概率(大约为 1/10 的概率)启动

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1580859/mspm0g3507-uart-interrupt-fail-to-start-with-a-certain-probability-approximately-1-10-probability

器件型号: MSPM0G3507

初始化并启用 UART 中断后、我们打印以下两个参数、发现当参数为 0x400 和 0 时、UART 中断可能正常运行;当参数为 0x400 和 0x400 时、UART 中断无法进入(此时,重新初始化并启用 UART 中断、打印参数仍然为 0x400 和 0x400、UART 中断仍然无法进入)。
此问题是概率性的(概率约为十分之一)。

UART 中断无效:
  0x400 = DL_UART_getEnabledInterrupts (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX);
  0x400 = DL_UART_getEnabledInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX);

UART 中断有效:
  0x400 = DL_UART_getEnabledInterrupts (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX);
  0 = DL_UART_getEnabledInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX);

这是我们的 UART 中断初始化和中断函数:

image.png

image.png

image.png

image.png

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

    您好、

    您遇到任何错误吗?

    马修

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    发生问题时、UART_2-INST-IRQHandler 中断处理函数的所有情况选项均无法进入(包括默认值)。
    当前的问题是、UART 中断可能无法输入(大约 1/10 概率)、而其他功能似乎是正常的 (GPIO 控制/I2C/ADC...)
    带有 0x400 和 0 的函数 DL_UART_getEnabledInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX) 的返回值分别代表什么?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、

    如果您尝试一些 UART 示例代码、是否会看到相同的行为?

    马修

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

    我们发现使用 UART 示例代码时的行为不同。

    我们只想:

    1.为什么函数“DL_UART_getEnabledInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX)“的返回值与发生这种现象时的正常情况不同?
    2.发生此问题时、返回值 0 是否表示 UART 中断无法正常启动?
    3.发生此问题时可以正常重新激活 UART 中断吗?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    在哪里(在您的代码中)采样  EnabledInterrupts 和 EnabledInterruptStatus ? 是在 ISR 还是过程级代码中?

    在过程级别(假设在 NVIC 中启用了 UART IRQ)、实际上无法看到这种组合、因为一旦发生这种组合、就会调用 ISR 并清除中断。

    (NVIC) ISPR[0]和 ISER[0]的值可能提供信息。

    您的 ISR 似乎也接受了“错误“中断;您是否启用了这些中断?

    -----

    非邀约:我建议您从 ISR 中删除以下行:

    >DL_UART_Main_clearInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX);

    读取 IIDX 寄存器已清除该状态、因此这可能产生的唯一影响是丢失后续字节。

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

    您好、

    让我检查一下、然后我会回复您

    马修

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

    谢谢、我们已在 ISR 中删除了函数“DL_UART_Main_clearInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX)“、但该行为仍然存在。
    通过直接初始化 UART 中断并在 MCU 上电后启用它、我们不会遇到此现象。 只有通过再次初始化和启用它或再次启用 UART 中断、才会发生该现象。
    我们是投影仪制造商、当投影仪处于待机模式时启用 MCU UART 中断;打开投影仪后、我们将禁用 MCU UART 中断;当投影仪稍后返回待机模式时、需要重新启用 MCU UART、这可能会导致出现这种现象。

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

    简短答案:在 DL_init 函数中、在 NVIC_ClearPendingIRQ 调用之前的某个时间、插入类似的内容:


    >(void) DL_UART_Main_receiveData (UART_2_INST);//读取并丢弃(和清除状态)


    更长的答案:我可以通过清除 UART IRQ(待处理时)来生成您的症状。 外设 (MIS) 指示了 Rx 中断、但 NVIC 不知道它、随后由于已设置 RIS 位、UART 没有向 NVIC 发出新的 IRQ(也称为“卡住“)。


    TRM (SLAU846C) 第 3.3.1.1 节(“设置和清除挂起中断状态“小节)描述了外设 IRQ 的“级别“行为、但至少在这种情况下 (UART RX)、结果似乎与“脉冲“行为一致、一旦清除挂起状态、就不会重新发出该结果。


    如果在调用 DL_init 时收到了一个字节、并且在 ClearPendingIRQ 之前完成了一段时间、则可能会遇到这种情况。 (浮点算术可能会扩展此处的窗口。)


    常见的权变措施是在启用 RX 中断位 (RIS) 之前显式清除该位。 但我观察到、当接收器继续运行时、仍然不会发出 RX 中断(结果:RXDATA 以静默方式溢出)。 这大概是因为尚未读取 RXDATA。


    最后清除内容的步骤是读取 RXDATA 寄存器(在本例中,我使用 OVRERR 来执行此操作)。 这也具有清除 RX 中断位的效果。 我建议您在启用中断时随意操作。


    此测试程序可以在其挂起(在 NVIC 中)时每清除第 4 个 RX 中断。 输入终端仿真器时、每第 4 个/第 5 个字节不会回显、第一个字节是因为 IRQ 被清除、第二个字节是由于 OVRERR。 但 OVRERR 代码会清除条件、然后恢复正常。

    c 文件:

    /*
     * This started life as the uart_echo_interrupts_standby example
     */
    #include "ti_msp_dl_config.h"
    volatile uint32_t ctr;
    volatile uint8_t gEchoData = 0;
    int main(void)
    {
        SYSCFG_DL_init();
        DL_UART_Main_enableInterrupt(UART_0_INST,
                                  DL_UART_MAIN_INTERRUPT_OVERRUN_ERROR |
                                     DL_UART_MAIN_INTERRUPT_RX);
    
        NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
        NVIC_EnableIRQ(UART_0_INST_INT_IRQN);
        
        while (1) {
            __disable_irq(); // hold it off
            __WFI();
            asm volatile(" nop ");   // Breakpoint here
            ++ctr;
            if ((ctr & 0x03) == 0)
            {
                // Every 4th byte:
                DL_UART_clearInterruptStatus(UART_0_INST, DL_UART_INTERRUPT_RX );
                NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);
            }
            __enable_irq(); // let it run
            asm volatile(" nop ");   // Keep the compiler from unrolling
        }
    }
    
    void UART_0_INST_IRQHandler(void)
    {
        asm volatile(" nop "); // breakpoint here
        switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {
            case DL_UART_MAIN_IIDX_RX:
                DL_GPIO_togglePins(GPIO_LEDS_PORT,
                    GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN);
                gEchoData = DL_UART_Main_receiveData(UART_0_INST);
                DL_UART_Main_transmitData(UART_0_INST, gEchoData);
                //DL_UART_clearInterruptStatus(UART_0_INST, DL_UART_INTERRUPT_RX );
                break;
            case DL_UART_IIDX_OVERRUN_ERROR:
                gEchoData = DL_UART_Main_receiveData(UART_0_INST);
                break;
            default:
                break;
        }
    }
    

    .syscfg:

    /**
     * These arguments were used when this file was generated. They will be automatically applied on subsequent loads
     * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
     * @cliArgs --device "MSPM0G350X" --part "Default" --package "LQFP-64(PM)" --product "mspm0_sdk@2.07.00.06"
     * @v2CliArgs --device "MSPM0G3507" --package "LQFP-64(PM)" --product "mspm0_sdk@2.07.00.06"
     * @versions {"tool":"1.25.0+4268"}
     */
    
    /**
     * Import the modules used in this configuration.
     */
    const GPIO   = scripting.addModule("/ti/driverlib/GPIO", {}, false);
    const GPIO1  = GPIO.addInstance();
    const SYSCTL = scripting.addModule("/ti/driverlib/SYSCTL");
    const UART   = scripting.addModule("/ti/driverlib/UART", {}, false);
    const UART1  = UART.addInstance();
    
    /**
     * Write custom configuration values to the imported modules.
     */
    GPIO1.$name                          = "GPIO_LEDS";
    GPIO1.associatedPins.create(2);
    GPIO1.associatedPins[0].$name        = "USER_LED_1";
    GPIO1.associatedPins[0].assignedPort = "PORTA";
    GPIO1.associatedPins[0].initialValue = "SET";
    GPIO1.associatedPins[0].assignedPin  = "0";
    GPIO1.associatedPins[0].pin.$assign  = "PA0";
    GPIO1.associatedPins[1].$name        = "USER_TEST";
    GPIO1.associatedPins[1].assignedPort = "PORTA";
    GPIO1.associatedPins[1].assignedPin  = "15";
    GPIO1.associatedPins[1].initialValue = "SET";
    
    const Board           = scripting.addModule("/ti/driverlib/Board", {}, false);
    Board.configureUnused = true;
    
    SYSCTL.forceDefaultClkConfig = true;
    SYSCTL.clockTreeEn           = true;
    
    UART1.$name                    = "UART_0";
    UART1.rxFifoThreshold          = "DL_UART_RX_FIFO_LEVEL_ONE_ENTRY";
    UART1.enableDMARX              = false;
    UART1.enableDMATX              = false;
    UART1.targetBaudRate           = 115200;
    UART1.enabledInterrupts        = ["OVERRUN_ERROR","RX"];
    UART1.peripheral.$assign       = "UART0";
    UART1.peripheral.rxPin.$assign = "PA11";
    UART1.peripheral.txPin.$assign = "PA10";
    UART1.txPinConfig.$name        = "ti_driverlib_gpio_GPIOPinGeneric0";
    UART1.rxPinConfig.$name        = "ti_driverlib_gpio_GPIOPinGeneric1";
    
    /**
     * Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future
     * version of the tool will not impact the pinmux you originally saw.  These lines can be completely deleted in order to
     * re-solve from scratch.
     */
    GPIO1.associatedPins[1].pin.$suggestSolution = "PA15";
    Board.peripheral.$suggestSolution            = "DEBUGSS";
    Board.peripheral.swclkPin.$suggestSolution   = "PA20";
    Board.peripheral.swdioPin.$suggestSolution   = "PA19";
    

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

    抱歉、 我们已经实施了您提供的对策、但这种现象仍然存在。
    在进行 200 次应力测试后、我们再次发现此问题。
    以下是我们根据您提供的解决方案所做的更正记录(这些是 Uart2 的初始化和中断处理功能,左侧为原始写入内容,右侧为您的说明和代码所做的修改)。 请帮助确认是否存在任何写入错误或您提到的对策未正确导入:

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

    是的、这是我建议的权变措施。 听起来这降低了发生频率;可能有不止一种方法(未显示?) 这种情况。

    即使您丢弃结果、您也应该指向始终读取 RXDATA 中的 RX 或 OVRERR 中断。 我的实验 表明、如果不这样做、会导致出现类似(但不完全相同)的情况。 我看到 RX 和 OVRERR 都有临界情况 (缓冲区溢出?) 这种情况下可能无法读取 RXDATA。

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

    我是否可以理解为:
    当 “DL_UART_getEnabledInterrupts (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX)“和 “DL_UART_getEnabledInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX)“的返回值都是 0x400 时、我需要调用“DL_UART_Main_receiveData (UART_2_INST“函数?
    如果是这种情况、我会编写一个 投票函数进行判断和读取、但这是否会导致任何其他问题(如频繁调用函数“**** getEnabledInterrupts***“导致中断异常或数据丢失)?

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

    当 “DL_UART_getEnabledInterrupts (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX)“和 “DL_UART_getEnabledInterruptStatus (UART_2_INST、DL_UART_MAIN_INTERRUPT_RX)“的 返回值都是 0x400 时、我调用“DL_UART_Main_receiveData (UART_2_INST)、但仍可正确输入两个中断值、但仍会出现问题。

    根据我们的理解、调用函数“DL_UART_Main_receiveData (UART_2_INST)“后的返回值应为 0x400 和 0。 为什么它们仍然都是 0x400?

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

    在过程级代码(启用所有中断)中、几乎(应该)不可能看到这种组合、因为在发生这种组合时、会调用 ISR、这会清除 RIS(以及 MIS)位。

    在 ISR 或重新初始化函数(其中 UART 中断在 NVIC 中被禁用)中、看到这种组合并不会令人惊讶、因为中断的呈现会被阻止。

    (据我所理解)您在过程级代码中看到这种组合(启用了所有中断)这一事实意味着到 NVIC 的 IRQ 已丢失(很可能是已清除)。 我在您发布的代码中看到了一种可能会发生这种情况的方式、但或许在其他地方会发生类似的事情?

    如您所述、您可以将条件作为安全/恢复机制进行轮询(尽管显然最好避免)。 这三个函数调用的成本相当低 — 每个都只读取一个 UART 寄存器(分别为 IMASK/MIS/RXDATA)、RXDATA 是唯一一个具有任何副作用的寄存器。