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.

[参考译文] TMS320F280039C:清除 EPG 中断标志

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

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1342753/tms320f280039c-epg-interrupt-flag-clearing

器件型号:TMS320F280039C

您好!

我正在尝试使用 EPG 外设驱动信号输出、以控制一组 WS2812 LED。 我正在尝试发送8个数据位、该数据位使用以3倍的速度运行的图形发生器的24位来生成图形、然后使用 EPG 中断向 EPG 馈送新数据、以便针对每个数据字节发送。

我最初的方法是使用 DMA、但我不确定是否甚至可以使用 EPG。 EPG 基址表列出了 DMA 无法访问寄存器、不过 DMA 方框图(图12-1)显示 EPG 已连接到 DMA 总线。

我已切换到使用 EPG 中断、但我只能获得一次中断触发、并且无法获取要清除的 GINTSTS INT 位、以便再次触发中断。 我的更新函数应该触发信号发生器开始成功发送每次运行时的前24位图形数据、但 ISR 仅运行一次、在我必须复位 CPU 以清除标志之前。

以下是我用于设置 EPG 的代码:

    EPG_selectEPGOutput(EPG1_BASE, EPG_OUT0, EPG_OUT_SEL_SIG);
    EPG_selectSignalOutput(EPG1_BASE, EPG_OUT0, EPG_SIGGEN0_DATATRANOUT3);

    // Bit period is 1.25us and there are three periods per bit. 120 MHz sysclk / 49 = 2.45 MHz
    // which gets pretty close.
    EPG_setClkGenPeriod(EPG1_BASE, EPG_CLKGEN0, 49U);

    EPG_selectSigGenClkSource(EPG1_BASE, EPG_SIGGEN0, EPG_CLKGEN0_CLKOUT0_GCLK);

    EPG_setClkGenOffset(EPG1_BASE, EPG_CLKGEN0, 0, 0);

    EPG_setDataBitLen(EPG1_BASE, EPG_SIGGEN0, 24);
    EPG_setSignalGenMode(EPG1_BASE, EPG_SIGGEN0, EPG_SIGGEN_MODE_SHIFT_LEFT_REPEAT);

    EPG_enableGlobal(EPG1_BASE);

此代码每10ms 运行一次、以发送图形发生器中的第一个数据。 正在正确发送初始24位。

    EPG_setData0Word(EPG1_BASE, EPG_SIGGEN0, ws2812_color);

    EPG_setSignalGenMode(EPG1_BASE, EPG_SIGGEN0, EPG_SIGGEN_MODE_SHIFT_LEFT_REPEAT);
    Interrupt_enable(INT_SYS_ERR);
    EPG_clearInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_FILL | EPG_INT_SIGGEN0_DONE);
    SysCtl_clearInterruptStatus(SYSCTL_SYS_ERR_INT_CLR_GINT | SYSCTL_SYS_ERR_INT_CLR_EPG1_INT);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
    EPG_enableInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_FILL);
    EPG_enableSignalGen(EPG1_BASE, EPG_SIGGEN0);

这是我的 ISR、它似乎只运行一次、无法清除 GINTSTS 寄存器中的 INT 状态。

__interrupt void ws2812_isr(void)
{
    uint32_t epg_int_status = EPG_getInterruptStatus(EPG1_BASE);
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
    SysCtl_clearInterruptStatus(SYSCTL_SYS_ERR_INT_CLR_GINT | SYSCTL_SYS_ERR_INT_CLR_EPG1_INT);
    EPG_clearInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_FILL | EPG_INT_SIGGEN0_DONE);

    if (epg_int_status & EPG_INT_SIGGEN0_FILL) {
        uint16_t pixel = data_counter / 3;
        uint16_t color = data_counter % 3;
        data_counter += 1;
        if (pixel < PIXEL_COUNT) {
            uint32_t ws2812_color;
            if (color == 0) {
                ws2812_color = color_to_ws2812(led_data[pixel].green);
            } else if (color == 1) {
                ws2812_color = color_to_ws2812(led_data[pixel].red);
            } else {
                ws2812_color = color_to_ws2812(led_data[pixel].blue);
            }

            EPG_setData0Word(EPG1_BASE, EPG_SIGGEN0, ws2812_color);
            EPG_setData1Word(EPG1_BASE, EPG_SIGGEN0, 0x00);
        } else {
            // EPG_setSignalGenMode(EPG1_BASE, EPG_SIGGEN0, EPG_SIGGEN_MODE_SHIFT_RIGHT_ONCE);
            // EPG_disableSignalGen(EPG1_BASE, EPG_SIGGEN0);
        }
    }
}

我已经单步执行了 ISR 和触发函数中的代码、其中 EPG_clearInterruptFlag 被调用、而 GINTSTS INT 标志从未被清除。 有时会清除 SIGGEN0_FILL 位、但 INT 标志本身仍保持设置状态。 关于清除标志或如何向 EPG 提供连续数据、我是否遗漏了某些内容。 如果我可以让 DMA 方法有效、而不是首选的中断。

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

    Kevin 老师好!

    在中断时:看起来您的代码已清除 DONE 和 Fill 标志、但并不清除全局中断标志。

    EPG_clearInterruptFlag(EPG_INT_GLOBAL_INT | EPG_INT_SIGGEN0_DONE | EPG_INT_SIGGEN0_FILL);

    这将正确清除中断标志。

    我正在咨询我们的设计团队、试图确认 DMA 引发器是否可以访问 EPG 寄存器。 我将在获得更多信息后回复。

    此致、
    伊袋

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

    DMA 权访问 EPG 寄存器。 在这方面、方框图不正确;我要提交一个 TT、以便在文档中进行修复。

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

    我已经对代码进行了一些更新、包括清除 EPG_INT_GLOBAL_INT、但仍然无法获得所需的中断标志。

    现在这是我的初始化代码。 我修改了它、从 EPG 一次写入60位。

        EPG_selectEPGOutput(EPG1_BASE, EPG_OUT0, EPG_OUT_SEL_SIG);
        EPG_selectSignalOutput(EPG1_BASE, EPG_OUT0, EPG_SIGGEN0_DATATRANOUT0);
    
        // Bit period is 1.25us and there are three periods per bit. 120 MHz sysclk / 49 = 2.45 MHz
        // which gets pretty close.
        EPG_setClkGenPeriod(EPG1_BASE, EPG_CLKGEN0, 49U);
    
        EPG_selectSigGenClkSource(EPG1_BASE, EPG_SIGGEN0, EPG_CLKGEN0_CLKOUT0_GCLK);
    
        EPG_setClkGenOffset(EPG1_BASE, EPG_CLKGEN0, 0, 0);
    
        EPG_setDataBitLen(EPG1_BASE, EPG_SIGGEN0, 60);
        EPG_setSignalGenMode(EPG1_BASE, EPG_SIGGEN0, EPG_SIGGEN_MODE_SHIFT_RIGHT_REPEAT);
    
        EPG_enableGlobal(EPG1_BASE);
        EPG_enableSignalGen(EPG1_BASE, EPG_SIGGEN0);
    
        Interrupt_enable(INT_SYS_ERR);
        EPG_enableInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_DONE);
    

    我每100ms 运行一次的触发代码是

        data_counter = 1;
    
        EPG_clearInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_FILL | EPG_INT_SIGGEN0_DONE | EPG_INT_GLOBAL_INT);
        SysCtl_clearInterruptStatus(SYSCTL_SYS_ERR_INT_CLR_GINT | SYSCTL_SYS_ERR_INT_CLR_EPG1_INT);
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
    
        EPG_setData0Word(EPG1_BASE, EPG_SIGGEN0, ws2812_data[0] & 0xFFFFFFFF);
        EPG_setData1Word(EPG1_BASE, EPG_SIGGEN0, ws2812_data[0] >> 32);

    我的 ISR 是:

    __interrupt void ws2812_isr(void)
    {
        GPIO_writePin(21, 1);
        uint32_t epg_int_status = EPG_getInterruptStatus(EPG1_BASE);
    
    
        if (epg_int_status & EPG_INT_SIGGEN0_DONE) {
            if (data_counter < 4) {
                EPG_setData0Word(EPG1_BASE, EPG_SIGGEN0, ws2812_data[data_counter] & 0xFFFFFFFF);
                EPG_setData1Word(EPG1_BASE, EPG_SIGGEN0, ws2812_data[data_counter] >> 32);
            }
            data_counter += 1;
        }
    
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
        SysCtl_clearInterruptStatus(SYSCTL_SYS_ERR_INT_CLR_GINT | SYSCTL_SYS_ERR_INT_CLR_EPG1_INT);
        EPG_clearInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_FILL | EPG_INT_SIGGEN0_DONE | EPG_INT_GLOBAL_INT);
        GPIO_writePin(21, 0);
    }
    

    我已将一个逻辑分析仪连接到 GPIO 引脚、每次 ISR 运行时我都会切换该引脚、ISR 的运行似乎比预期的要多:

    红色信号的每一个微小点即表示 ISR 正在运行、而橙色信号则表示 EPG 的输出。 看起来仍然像 EPG 中断未清除或立即重新触发。 这是与我如何清除中断标志相关的问题还是与 EPG 配置相关的问题?

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

    Kevin 老师好!

    我需要更深入地了解这一点、我很快就会回复。

    谢谢。
    伊袋

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

    尊敬的 Ibukun:

    您是否获得了有关此方面的更多信息?

    谢谢。

    凯文

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

    尊敬的 Kevin:

    进行了更多的研究。  对于 SYS_ERR 中断、必须 在 清除 gint 之前清除源标志。 在 EPG 中断处理程序(SYS_ERR 处理程序、我假定)中、首先清除 gint、然后清除 EPG 标志。 这将导致立即再次设置 GINT 并阻止触发新的中断。

    此致、
    伊袋

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

    尊敬的 Ibukun:

    我已经重新安排中断清除、如下所示:

    __interrupt void ws2812_isr(void)
    {
        GPIO_writePin(21, 1);
        uint32_t epg_int_status = EPG_getInterruptStatus(EPG1_BASE);
    
    
        if (epg_int_status & EPG_INT_SIGGEN0_DONE) {
            if (data_counter < 4) {
                EPG_setData0Word(EPG1_BASE, EPG_SIGGEN0, ws2812_data[data_counter] & 0xFFFFFFFF);
                EPG_setData1Word(EPG1_BASE, EPG_SIGGEN0, ws2812_data[data_counter] >> 32);
            }
            data_counter += 1;
        }
    
        EPG_clearInterruptFlag(EPG1_BASE, EPG_INT_SIGGEN0_FILL | EPG_INT_SIGGEN0_DONE | EPG_INT_GLOBAL_INT);
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
        SysCtl_clearInterruptStatus(SYSCTL_SYS_ERR_INT_CLR_GINT | SYSCTL_SYS_ERR_INT_CLR_EPG1_INT);
        GPIO_writePin(21, 0);
    }
    

    通过这个改动、我仍然看到中断在它们不应该发生的时候被激活。 我能够对相关内容进行更改、以便我们在不需要中断时禁用这些中断、从而使这些中断在我的应用中正常工作。 但是、我仍然认为中断持续触发有一件事情不是很正确。

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

    Kevin 老师好!

    您已将 EPG SIGGEN 配置为 SHIFT_RIGHT_REPEAT 模式。 在此模式下、每次信号发生器完成移出配置的位长度时、DONE 信号会变为高电平、并触发中断、模式会再次启动。 这会持续发生。

    如果不希望图形持续移出、则应将模式更改为 SHIFT_RIGHT_once、然后在需要将 SIGGEN_CTL0.EN 设置为1。

    如果您想要它连续移出、但又不希望中断在每个 BITLENGTH 位结束时触发、那么您需要不同的中断源(例如 CPU 计时器)。 如果在重复模式下使用 EPG 中断、只要 EN = 1、它就会始终在每个 BITLENGTH 时钟周期触发中断。

    这有什么用吗?

    此致、
    伊袋

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

    尊敬的 Ibukun:

    我认为这是合理的、需要澄清几个问题:

    我试图向 SIGGEN_DATA 寄存器发送多个连续写入、然后在这些写入之后停止、直至其再次启动、因此我应该对除最后一个传输之外的每个传输使用 SHIFT_RIGHT_REPEAT、还是应该对所有这些传输都使用 SHIFT_RIGHT_ONSE 中?

    我希望该行为在我不断写入新数据的情况下移出、然后在没有写入新数据的情况下停止移动。

    谢谢。

    凯文

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

    在 SHIFT_RIGHT_UNce 模式下、EPG 会在完成移出位并设置 EN=0后停止。 您需要向 EN 写入1以使其移出下一个值。 这在您已经有的实现方案中将起作用、只需在更新数据后写入 EN=1即可开始移动下一个字。

    但是、如果您希望避免位移位之间存在任何周期间隙、这会使它变得更加复杂一些。 在 SHIFT_RIGHT_REPEAT 模式下、您只需确保在移位完成之前更新数据系的值、并且下一个移位会使用新的数据系值。 然后您可以在写入最后一个值后设置 EN=0、但我想您必须找到一种方法来为写入 EN 设置计时、以便 SIGGEN 在数据模式开始重复之前停止。

    请注意、在更改模式之前、必须设置 EN=0。  不支持在信号发生器主动对位进行移位时更改模式。

    此致、
    伊袋

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

    好的、这一切都很合理、也解释了我看到的行为。 似乎我使用的方法是将其保持在重复模式、而在我不想馈送更多数据时禁用中断是我们的最佳方法。 对于此操作、最后一位始终为0、因此重复发送该位对该应用程序而言不是问题。

    非常感谢这方面的帮助!