hi, i have a problem with the TM4C1294 microcontroller when using DMA with the SSI peripheral and interrupts.
For a LED driver project (using many TLC5971) i need a constant stream of data (overall ~20k Bytes of data, every 30ms), so I decided to use the SSI with the DMA in pingpong mode, because i need more then 1024 elements to be transfered to the SSI and I need nearly zero gap (max. 8 SSI clock cycles gap).
After the first transfer of the 1024 elements of the PRI channel I want to set the next data to be sent for the PRI channel (while the ALT channel is transferring the data to the SSI) in the SSI DMA TX complete interrupt handler.
My problem is now with the SSI interrupt handler, which is called after the first 1024 Bytes from the PRI channel. I cannot clear the interrupt flag correctly, therefore the ISR is called in an endless loop.
I enabled the SSI_DMATX interrupt (SSIIntEnable(SSI0_BASE,SSI_DMATX)) to be able to set the next data bytes to be sent to the SSI for the ALT or PRI channel of the DMA controller.
At the beginning of the SSI interrupt handler i clear the interrupt flag (SSIIntClear(SSI0_BASE,SSIIntStatus(SSI0_BASE, true)), i also tested it with SSIIntClear(SSI0_BASE,SSI_DMATX)) but i verified that it is not being cleared.
If i set the interrupt only to SSI_TXFF and/or SSI_TXEOT it works as expected, but I think SSI_DMATX is better to use for getting nearly zero gap.
I tested more hints for clearing the interrupt flag to prevent the endless loop:
In the ISR of the SSI I did the following (which I think was suggested in the datasheet of the uC at page 1240):
HWREG(SSI0_BASE + 0x0024)&=~SSI_DMACTL_TXDMAE;//clear txdmae in the ssidmactl
HWREG(SSI0_BASE + 0x20)|=(1<<5);//setting DMATXIC in SSIICR (clears interrupt)
This also didn't work. I also tested this:
SSIDMADisable(SSI0_BASE,SSI_DMA_TX);
SSIIntClear(SSI0_BASE,SSIIntStatus(SSI0_BASE, true));
SSIDMAEnable(SSI0_BASE,SSI_DMA_TX);
But after enabling the DMA in the SSI peripheral the interrupt flag is set, so I got also no luck.
Here is my initialization code snippet:
SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOA);
SysCtlGPIOAHBEnable(SYSCTL_PERIPH_SSI0);
SysCtlGPIOAHBEnable(SYSCTL_PERIPH_UDMA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
//Enable uDMA
uDMAEnable();
//Set uDMA control table
uDMAControlBaseSet(pui8ControlTable);
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinConfigure(GPIO_PA2_SSI0CLK);
GPIOPinConfigure(GPIO_PA4_SSI0XDAT0);///TX
GPIOPinConfigure(GPIO_PA5_SSI0XDAT1);//RX
GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_4 |
GPIO_PIN_5);
SSIClockSourceSet(SSI0_BASE,SSI_CLOCK_SYSTEM);
SSIDisable(SSI0_BASE);
SSIConfigSetExpClk(SSI0_BASE, 120000000, SSI_FRF_MOTO_MODE_0,
SSI_MODE_MASTER, 10000000, 8);
SSIEnable(SSI0_BASE);
SSIDMAEnable(SSI0_BASE, SSI_DMA_TX);
uDMAChannelAttributeDisable(UDMA_CH11_SSI0TX,
UDMA_ATTR_USEBURST |
UDMA_ATTR_REQMASK);
uDMAChannelAttributeEnable(UDMA_CH11_SSI0TX, UDMA_ATTR_HIGH_PRIORITY);
uDMAChannelAssign(UDMA_CH11_SSI0TX);
uDMAChannelControlSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT,
UDMA_SIZE_8 |
UDMA_SRC_INC_8 |
UDMA_DST_INC_NONE |
UDMA_ARB_4);
uDMAChannelTransferSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *) &data_ssi0[data_ssi0_actual_frame][0],
(void *)(SSI0_BASE + SSI_O_DR),
1024);
uDMAChannelControlSet(UDMA_CH11_SSI0TX | UDMA_ALT_SELECT,
UDMA_SIZE_8 |
UDMA_SRC_INC_8 |
UDMA_DST_INC_NONE |
UDMA_ARB_4);
uDMAChannelTransferSet(UDMA_CH11_SSI0TX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *) &data_ssi0[data_ssi0_actual_frame][1024],
(void *)(SSI0_BASE + SSI_O_DR),
1024);
IntEnable(INT_SSI0);
SSIIntEnable(SSI0_BASE,SSI_DMATX);
uDMAChannelAssign(UDMA_CH11_SSI0TX);
uDMAChannelEnable(UDMA_CH11_SSI0TX);
Here is my interrupt handler code snippet:
SSIIntClear(SSI0_BASE,SSIIntStatus(SSI0_BASE, true));
if(uDMAChannelModeGet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT) == UDMA_MODE_STOP) //set next data to be sent to the SSI
{
uDMAChannelTransferSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *) &data_ssi0[data_ssi0_actual_frame][data_ssi0_dma_counter],
(void *)(SSI0_BASE + SSI_O_DR),
1024);
}
if(uDMAChannelModeGet(UDMA_CH11_SSI0TX | UDMA_ALT_SELECT) == UDMA_MODE_STOP)
{
uDMAChannelTransferSet(UDMA_CH11_SSI0TX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *) &data_ssi0[data_ssi0_actual_frame][data_ssi0_dma_counter],
(void *)(SSI0_BASE + SSI_O_DR),
1024);
}
PS:
The uDMA table is aligned.
I'm using the connected launchpad TM4C1294XL with 120MHZ CPU clock (from PLL).
I use Keil MDK ARM V5 for compiling and debugging.
Any suggestions are welcome.