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.