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.

TM4C1294XL SSI uDMA clearing the RX buffer issue

I'm having an issue when reading from the SSI0 Interface using uDMA after a uDMA write (Two separate functions). The data in the uDMA pipeline always seems to be offset by a couple of bytes (due to the uDMA write). I was able to correct this issue by reading back 8 bytes when transmitting as shown in the code below. I always read back 8 bytes no matter of the uDMA tx transaction size. For instance my byte count varies from 1 to 1024. 

1. How many bytes should I read back after a uDMA write to clear the buffer preparing me for a uDMA read? These are two different functions. For instance, SPI_READ, SPI_WRITE. Currently reading back 8 bytes after every write fixed my issue but I want to know why? Does it have to do with the SPI FIFO buffer size?

2. Why isn't using the SSI read non blocking command sufficient to clear the FIFO when using uDMA? Below are code snippets and do not imply any consecutive order. 

/* SSI Clear buffer */
uint32_t discard; /* Clear RX buffer before initiating read */
		while(SSIDataGetNonBlocking(privateData->base, &discard));




/* Configure the RX channel primary control parameters */
		ROM_uDMAChannelControlSet(privateData->uDMARxChannel | UDMA_PRI_SELECT,
								  UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
								  UDMA_ARB_8);

		/* Configure the TX channel primary control parameters */
		ROM_uDMAChannelControlSet(privateData->uDMATxChannel | UDMA_PRI_SELECT,
								  UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
								  UDMA_ARB_8);


/* SPI WRITE */

	/* Prime the uDMA Tx Channel */
			ROM_uDMAChannelTransferSet(privateData->uDMATxChannel | UDMA_PRI_SELECT,
									   UDMA_MODE_BASIC,
									   txBuf,
									   (void *)(privateData->base + SSI_O_DR),
									   count);

			/* Prime the uDMA Rx Channel */
			ROM_uDMAChannelTransferSet(privateData->uDMARxChannel | UDMA_PRI_SELECT,
									   UDMA_MODE_BASIC,
									   (void *)(privateData->base + SSI_O_DR),
									   sharedBuf,
									   8);

			/* Enable DMA and start the SSI Transfer */
			ROM_uDMAChannelEnable(privateData->uDMARxChannel);
			ROM_uDMAChannelEnable(privateData->uDMATxChannel);







  • Hello CaliDanMan,

    It is not clear which device is being referred to here, but for legacy mode of operation (irrespective of the device), the SSI when transmits a data byte, it receives a data byte as well in the FIFO. The FIFO is 8 locations deep. So when clearing the FIFO you would need to read out 8 bytes.

    The Non-blocking read checks if the FIFO is not empty and reads out a byte. What it does not do is wait for the FIFO to be non-empty. If the uDMA Write and Read Transaction size is kept the same, then the uDMA shall always empty the FIFO.

    Regards

    Amit

  • Thanks for the reply. 

    I'm using the TM4C1294XL MCU. 

    You mention "If the uDMA Write and Read Transaction size is kept the same, then the uDMA shall always empty the FIFO." which I find to not be the case. For instance, if i use uDMA to write one byte. In the same function I also read one byte to clear the buffer(as you mentioned). I find that the buffer isn't actually cleared. I have to read out 8 bytes in order for the next uDMA transaction to return the correct results. 

    -Daniel

  • I thought I would share an application I wrote with the community on setting up a basic spi read / write using uDMA. This project started out from the TI udma_demo.c source. 

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_uart.h"
    #include "inc/hw_ssi.h"
    #include "driverlib/fpu.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    #include "driverlib/ssi.h"
    #include "utils/cpu_usage.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    #if defined(ewarm)
    #pragma data_alignment=1024
    uint8_t pui8ControlTable[1024];
    #elif defined(ccs)
    #pragma DATA_ALIGN(pui8ControlTable, 1024)
    uint8_t pui8ControlTable[1024];
    #else
    uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024)));
    #endif
    
    
    
    uint32_t g_ui32SysClock;
    
    static uint8_t txTrash[1024] = {0};
    static uint8_t rxTrash[1024] = {0};
    
    static uint32_t g_ui32uDMAErrCount = 0;
    static uint32_t g_ui32SSIRxCount = 0;
    static uint32_t g_ui32SSITxCount = 0;
    
    uint8_t txData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    uint8_t rxData[1024] = {0};
    
    static uint8_t spiWrite(uint8_t *data, const uint32_t count);
    static uint8_t spiRead(uint8_t *data, const uint32_t count);
    
    void uDMAErrorHandler(void)
    {
        uint32_t ui32Status;
    
        ui32Status = ROM_uDMAErrorStatusGet();
        if(ui32Status)
        {
            ROM_uDMAErrorStatusClear();
            g_ui32uDMAErrCount++;
        }
    }
    
    void SSI0IntHandler(void)
    {
    	uint32_t ui32Status;
    
    	ui32Status = ROM_SSIIntStatus(SSI0_BASE, 1);
    	ROM_SSIIntClear(SSI0_BASE, ui32Status);
    
    	if (ui32Status == SSI_TXEOT) // Interrupt due to TX FIFO empty
    	{
    		/* Do Amazing stuff here */
    	}
    }
    
    void InitSPITransfer(void)
    {
    
    	ROM_SSIDMAEnable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX);
    
    	ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0RX,UDMA_ATTR_ALL);
    
    	ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0TX,UDMA_ATTR_ALL);
    
    	ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI0TX, UDMA_ATTR_USEBURST);
    	ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI0RX, UDMA_ATTR_USEBURST);
    
    	ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT,
    							  UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
    							  UDMA_ARB_4); /* If UDMA_ARB_8 is used, I've noticed a premature return on SSIBusy. */
    
    	ROM_uDMAChannelControlSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
    							  UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
    							  UDMA_ARB_4); /* If UDMA_ARB_8 is used, I've noticed a premature return on SSIBusy. */
    
    	ROM_SSIIntEnable(SSI0_BASE, SSI_TXEOT);
    	ROM_IntEnable(INT_SSI0);
    
    }
    
    void ConfigureSSI0(void)
    {
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3);
        ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3);
        ROM_GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    	ROM_GPIOPinConfigure(GPIO_PA4_SSI0XDAT0);
    	ROM_GPIOPinConfigure(GPIO_PA5_SSI0XDAT1);
    	ROM_GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_2);
    
    	SSIConfigSetExpClk(SSI0_BASE, g_ui32SysClock, SSI_FRF_MOTO_MODE_3,
    			SSI_MODE_MASTER, 1000000, 8);
    
    	SSIEnable(SSI0_BASE);
    }
    
    void ConfigureUART(void)
    {
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
        ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
        UARTStdioConfig(0, 115200, g_ui32SysClock);
    }
    
    
    int main(void)
    {
    
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
        		SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);
    
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0);
    
        ConfigureUART();
    	UARTprintf("\033[2J\033[H");
    
    	ConfigureSSI0();
    
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
        ROM_IntEnable(INT_UDMAERR);
        ROM_uDMAEnable();
        ROM_uDMAControlBaseSet(pui8ControlTable);
        InitSPITransfer();
    
    	while(1)
        {
    		spiWrite(txData, 10);
    		spiRead(rxData, 1024);
    #if 0
    		uint16_t x;
    		for (x = 0; x < 1024; x++) {
    			UARTprintf("Byte %i, value %i\n", x, rxData[x]);
    		}
    #endif
    		UARTprintf("\033[2J\033[H");
    		UARTprintf("Rx transactions: %i\n", g_ui32SSIRxCount);
    		UARTprintf("Tx transactions: %i\n", g_ui32SSITxCount);
    
        	GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, GPIO_PIN_0);
            SysCtlDelay(g_ui32SysClock / 20 / 3);
            GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, 0);
            SysCtlDelay(g_ui32SysClock / 20 / 3);
        }
    }
    
    static uint8_t spiWrite(uint8_t *data, const uint32_t count)
    {
    	uint8_t returnValue = 0;
    
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
    							   UDMA_MODE_BASIC,
    							   data,
    							   (void *)(SSI0_BASE + SSI_O_DR),
    							   count);
    
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT,
    							   UDMA_MODE_BASIC,
    							   (void *)(SSI0_BASE + SSI_O_DR),
    							   rxTrash,
    							   count);
    
    	ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, 0);
    
    	ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0RX); /* Dump these bytes */
    	ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
    	while(SSIBusy(SSI0_BASE));
    
    	ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3);
    	g_ui32SSITxCount++;
    
    	return returnValue;
    }
    
    static uint8_t spiRead(uint8_t *data, const uint32_t count)
    {
    	uint8_t returnValue = 0;
    
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
    							   UDMA_MODE_BASIC,
    							   txTrash,
    							   (void *)(SSI0_BASE + SSI_O_DR),
    							   count);
    
    	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT,
    							   UDMA_MODE_BASIC,
    							   (void *)(SSI0_BASE + SSI_O_DR),
    							   data,
    							   count);
    
    	ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, 0);
    
    	ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0RX);
    	ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
    	while(SSIBusy(SSI0_BASE)); /* This will return early if Arbitration is set to 8 */
    
    	ROM_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3);
    
    	g_ui32SSIRxCount++;
    
    	return returnValue;
    }
    

  • There is one bug with the application above. If the "spiWrite" function is called to transfer 6 packets, then the spi read results will be incorrect. Make sure you set the bytes to read and dump to a minimum of 8 to fix this issue. 

  • Hello CaliDanMan,

    Now that is not correct. If the uDMA transfers same number of byte to and from SSI, the SSI RX FIFO should all be cleared. I can try the code you have sent (thanks for the same).

    Regards

    Amit

  • Hello CaliDanMan,

    Did you ever finish your SSI/uDMA example? I see you made a couple posts about it in September, it would be great if you could throw a finished version up on github.

    Regards,
    Michael