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 interrupt flag clearing

Part Number: TMS320F280039C

Hi,

I am attempting to use the EPG peripheral to drive signals out for controlling a set of WS2812 LEDs. I am trying to send 8 data bits which uses 24 bits of the pattern generator running at 3x the speed to generate the pattern and then using the EPG interrupt to feed the EPG with new data to send for each byte of data.

My initial approach was to use DMA but I am unsure if that is even possible with the EPG. The EPG Base Address Table lists that the DMA cannot access the registers however the DMA block diagram (Figure 12-1) shows that the EPG is connected to the DMA bus.

I have switched to using the EPG interrupts but I am only able to get the interrupt to trigger once and I cannot get the GINTSTS INT bit to clear so that the interrupt can trigger again. My update function that is supposed to trigger the signal generator to start sending successfully sends the first 24 bits of pattern data every time it runs but the ISR only runs once before I have to reset the CPU to get the flag cleared.

Here is the code that I am using to setup the 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);

This is the code that runs every 10ms that sends the first data in the pattern generator. The initial 24 bits are being sent correctly.

    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);

And this is my ISR which only seems to run once and fails to clear the INT status in the GINTSTS register.

__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);
        }
    }
}

I have stepped through the code in both the ISR and the trigger function where the EPG_clearInterruptFlag is called and the GINTSTS INT flag is never cleared. Sometimes the SIGGEN0_FILL bit gets cleared but the INT flag itself stays set. Is there something I am missing with regards to clearing the flag or how the EPG is supposed to be fed with continuous data. If I could get a DMA approach working instead of the interrupts that would be preferred.

  • Hello Kevin,

    On the interupt: it looks like your code clears the DONE and FILL flags but does not clear the global interrupt flag.

    EPG_clearInterruptFlag(EPG_INT_GLOBAL_INT | EPG_INT_SIGGEN0_DONE | EPG_INT_SIGGEN0_FILL);

    This should clear the interrupt flags correctly.

    I am consulting with our design team to attempt to confirm if the DMA initiator has access to EPG registers. I will revert once I have more information.

    Best regards,
    Ibukun

  • The DMA does not have access to the EPG registers. The block diagram is incorrect in this regard; I am filing a ticket to have this fixed in the documentation.

  • I have made some updates to the code including clearing the EPG_INT_GLOBAL_INT and I am still not able to get the interrupt flags to behave as I would expect.

    This is my initialization code now. I have modified it to write 60 bits at a time out of the EPG.

        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);
    

    My trigger code that runs every 100ms is

        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);

    and my ISR is:

    __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);
    }
    

    I have connected a logic analyzer to a GPIO pin that I am toggling every time the ISR runs and it appears that the ISR is running way more than it should:

    Every blip of the red signal is the ISR running while the orange is the output of the EPG. It is still looking like the EPG interrupts are either not clearing or are immediately re-triggering. Is this an issue with how I am clearing the interrupt flags or is this an issue with the EPG configuration?

  • Hello Kevin,

    I will need to look into this a little bit more, and I will revert shortly.

    Thanks,
    Ibukun

  • Hi Ibukun,

    Have you gotten any more info on this?

    Thanks,

    Kevin

  • Hi Kevin,

    Did a little more research here. For SYS_ERR interrupts, the source flag must be cleared before clearing GINT. In your EPG interrupt handler (SYS_ERR handler I assume), you clear GINT first, then you clear the EPG flag. This would cause GINT to be set again immediately and prevent new interrupts from being fired.

    Best regards,
    Ibukun

  • Hi Ibukun,

    I have rearranged the interrupt clears as shown below:

    __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);
    }
    

    With this change, I am still seeing the interrupts fire when they are not supposed to. I was able to change some things around so that we are disabling the interrupts when they are not needed so I have gotten this working for my application. I still think there is something not quite right going on with the interrupts continuously firing though.

  • Hello Kevin,

    You have the EPG SIGGEN configured in SHIFT_RIGHT_REPEAT mode. In this mode, every time the signal generator finishes shifting out the length of bits configured, the DONE signal goes high and the interrupt is fired, and the pattern starts again. This happens continuously.

    If you don't want the pattern shifted out continuously, then you should change the MODE to SHIFT_RIGHT_ONCE, and then you set SIGGEN_CTL0.EN to 1 when you want it to shift out.

    If you want it shifted out continuously, but you don't want the interrupt to fire at the end of every BITLENGTH bits, then you need a different interrupt source (e.g. CPU Timer). If you use the EPG interrupt in repeat mode, it will always fire the interrupt every BITLENGTH clock cycles as long as EN = 1.

    Does this help?

    Best regards,
    Ibukun

  • Hi Ibukun,

    I think that makes sense, couple of clarifying questions:

    Since I am attempting to send multiple successive writes to the SIGGEN_DATA register and then stop after those writes until it is initiated again, should I be using SHIFT_RIGHT_REPEAT for every transfer except the last one or should it be in SHIFT_RIGHT_ONCE for all of them?

    I want the behavior to be it shifts out as long as I keep writing in new data and then it stops shifting once no new data is written.

    Thanks,

    Kevin

  • In SHIFT_RIGHT_ONCE mode, the EPG will stop once it has finished shifting out the bits and set EN=0. You would need to write 1 to EN to make it shift out the next value. This would work in the implementation you already have, just write EN=1 after updating DATAIN to start shifting the next word.

    If you want to avoid any cycle gap between bit shifts though, that makes it a little more complicated. In SHIFT_RIGHT_REPEAT mode, you could just make sure the DATAIN values are updated before shifting completes, and the next shift will use the new DATAIN values. Then you could set EN=0 after writing the last value, but I think you would have to find a way to time the write to EN such that the SIGGEN stops before the data pattern starts to repeat itself.

    Note that you have to set EN=0 before changing the MODE. Changing MODE while the signal generator is actively shifting bits is not supported.

    Best regards,
    Ibukun

  • Ok, that all makes sense and explains the behavior that I am seeing. Sounds like the approach I am using of keeping it in repeat mode and disabling the interrupts when I don't want to feed more data is the best we can do. The last bit is always 0 for this so the repeated sending of that is not a problem for this application.

    Thanks so much for the help on this!