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.

External ADC interface with TIVA DMA

Other Parts Discussed in Thread: EK-TM4C123GXL, ADS1255

Hi,

I am trying to interface TM4C123G (launch pad - EK-TM4C123GXL board) with 24 bit ADC (ADS1255).  Since our application requires the ADC to run @ 7.5KSPS (min), I am trying to explore the possibility of using TIVA DMA controller to interface with the ADC (as against using an interrupt based approach)

I am using the following approach to first configure the ADC and then use DMA to transfer/receive data from the ADC

a) Configure the uDMA controller
b) Use SSI0 (without DMA enabled) to write and read the ADC registers. Set up the ADC for "Read Data Continuously Mode (RDATAC)".
c) Configure the Pin PF4 as DMA trigger enable (ADS1255 DRDYB pin is connected to PF4). In setupDMATrigger()  - i have configured the DMA to transfer three bytes to the SSI0 TX register. I can see that this section of the code is working - could monitor the 24 clocks and serial data being shifted out in a scope.
d) The uDMAGPIOTriggerHandler continues to send 3 bytes through SSI0 TX interface every time it receives a trigger (DRDYB falling edge from ADC). No issues here as well.
e) I have setup DMA on SSI0 RX for ping-pong acquisition. The intent here is to receive the data from the  ADC directly into the ping-pong buffers. The relevant section of the code is in the function setupSSI0DMA.

In step e), i am not receiving the ADC data into the ping pong buffers. In fact the SSI0RXhandler is not getting triggered at all.

Can you please help review and let me know what i could be possibly missing?


Best regards,

Sridhar

----- Code

#include <stdint.h>
#include <stdbool.h>
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_uart.h"
#include "inc/hw_ssi.h"
#include "driverlib/fpu.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/ssi.h"
#include "driverlib/pin_map.h"
#include "driverlib/interrupt.h"
#include "driverlib/udma.h"

// GLOBALS
uint8_t prevDRDYBLevel;

uint8_t Spi_write_data;

// Cmd and Data to configure ADS1255
uint8_t ADCWREG[13] = {0x50, 0x0A, 0x02, 0x01, 0x27, 0xD0, 0xE1, 0x00, 0x00, 0x00, 0x08, 0xAC, 0x44};

// Split read registers command into two parts

// Part 1: Command
uint8_t ADCCMDREAD[2] = {0x10, 0x0A};
// Part 2: Actual Read
uint8_t ADCRREG[11] = {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

// Read Data Continuously
uint8_t RDATCREG =  {0x03};

// Ping-Pong Buffer definitions
#define UART_TXBUF_SIZE        256
#define  SPI_RXBUF_SIZE        256
static uint8_t g_pui8TxBuf[3] = {0xAA, 0xAA, 0xAA};
static uint8_t g_pui8RxPing[SPI_RXBUF_SIZE];
static uint8_t g_pui8RxPong[SPI_RXBUF_SIZE];

// Define errors counters
static uint32_t g_ui32BadISR = 0;
static uint32_t g_ui32DMAErrCount = 0;

// Define transfer counter
static uint32_t g_ui32MemXferCount = 0;

// Transfer counters
static uint32_t g_ui32RxPingCount = 0;
static uint32_t g_ui32RxPongCount = 0;

// The control table used by the uDMA controller.  This table must be aligned to a 1024 byte boundary.
#pragma DATA_ALIGN(pui8ControlTable, 1024)
uint8_t pui8ControlTable[1024];

// Library error routine
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

// uDMA transfer error handler
void uDMAErrorHandler(void)
{
	uint32_t ui32Status;

	// Check for uDMA error bit
	ui32Status = uDMAErrorStatusGet();

	// If there is a uDMA error, then clear the error and increment the error counter.
	if(ui32Status)
	{
		uDMAErrorStatusClear();
		g_ui32DMAErrCount++;
	}
}

// PF4 (DRDYB) uDMA Trigger interrupt handler
void uDMAGPIOTrigHandler(void)
{
	uint32_t ui32Mode;

	GPIOIntClear(GPIO_PORTF_BASE, GPIO_INT_DMA);

	// Check for the primary control structure to indicate complete.
	ui32Mode = uDMAChannelModeGet(UDMA_CH15_GPIOF);

	if(ui32Mode == UDMA_MODE_STOP)
	{
		// Increment the count of completed transfers.
		g_ui32MemXferCount++;

		// Configure it for another transfer.
		uDMAChannelTransferSet(UDMA_CH15_GPIOF, UDMA_MODE_AUTO,
				g_pui8TxBuf, (void *)(SSI0_BASE + SSI_O_DR),
				sizeof(g_pui8TxBuf));

		// Initiate another transfer.
		uDMAChannelEnable(UDMA_CH15_GPIOF);
	}

	// If the channel is not stopped, then something is wrong.
	else
	{
		g_ui32BadISR++;
	}
}


// SSI0 Receive Interrupt Handler
void SSI0RXHandler(void)
{
	uint32_t ui32Status;
	uint32_t ui32Mode;

	ui32Status = SSIIntStatus(SSI0_BASE, 1);

	SSIIntClear(SSI0_BASE, ui32Status);

	ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT);

	if(ui32Mode == UDMA_MODE_STOP)
	{
		g_ui32RxPingCount++;

		uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT,
				UDMA_MODE_PINGPONG,
				(void *)(SSI0_BASE + SSI_O_DR),
				g_pui8RxPing, sizeof(g_pui8RxPing));
	}

	ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT);

	if(ui32Mode == UDMA_MODE_STOP)
	{
		g_ui32RxPongCount++;

		uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT,
				UDMA_MODE_PINGPONG,
				(void *)(SSI0_BASE + SSI_O_DR),
				g_pui8RxPong, sizeof(g_pui8RxPong));
	}

	uDMAChannelEnable(UDMA_CHANNEL_SSI0RX);

}

void Spi_Read_write_8bit(unsigned char* PBuff,uint16_t Length,unsigned int uiBase)
{
	uint32_t temp = 0;
	uint8_t temp_count = 0;
	while(SSIDataGetNonBlocking(SSI0_BASE, &temp));
	for(temp_count = 0;temp_count < Length;temp_count++)
	{
		SSIDataPut(uiBase, *(PBuff));
		SSIDataGet(uiBase, &temp);
		*(PBuff++) = temp;
	}

}

void setupADCUsingSSI0(void)
{
	uint32_t temp;
	// SPI/SSI Configuration Code Start

	// Enable SSI0 Peripheral
	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_SSI0);

	// Enable the GPIO Port A, as we are using PA[5:2]
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_GPIOA);

	SysCtlDelay(10000);

	// Configure pin muxing
	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
	GPIOPinConfigure(GPIO_PA4_SSI0RX);
	GPIOPinConfigure(GPIO_PA5_SSI0TX);

	// Configure the GPIO settings for the SSI pins
	GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);

	// Configure the SSI mode and setup as master
	SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 2000000, 8);

	// Enable the SSI Module
	SSIEnable(SSI0_BASE);

	SysCtlDelay(3000000);

	// Read any residual data from the SSI Port

	while(SSIDataGetNonBlocking(SSI0_BASE, &temp))
	{
		// Nothing to do here
	}

	// Issue ADC Reset Command

	// Step1: Take CSB Low
	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00);
	Spi_write_data = 0xFE;
	Spi_Read_write_8bit(&Spi_write_data, 1, SSI0_BASE);
	SysCtlDelay(10000);

	// Step2:  Write ADC Registers
	Spi_Read_write_8bit(ADCWREG, 13, SSI0_BASE);
	SysCtlDelay(10000);

	// Step3: Write the command to READ Registers
	Spi_Read_write_8bit(ADCCMDREAD, 2, SSI0_BASE);
	SysCtlDelay(10000);

	// Step 4: Read Registers
	Spi_Read_write_8bit(ADCRREG, 11, SSI0_BASE);
	SysCtlDelay(10000);

	//Step 5: Try to catch the DRDBYB falling edge without using interrupts

	while(!GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4));

	prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4);

	while(prevDRDYBLevel)
	{
		prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4);
		SysCtlDelay(200);
	}

	Spi_Read_write_8bit(&RDATCREG, 1, SSI0_BASE);
	SysCtlDelay(10000);

}

void setupDMATrigger(void)
{

	// Note DRDYB is the uDMA Trigger, PF4
	// Configure and enable the GPIOF with DMA
	GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4);
	GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4, GPIO_FALLING_EDGE);
	GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU);
	GPIOIntClear(GPIO_PORTF_BASE, GPIO_PIN_4);

	GPIODMATriggerEnable(GPIO_PORTF_BASE, GPIO_PIN_4);

	GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_DMA);

	IntEnable(INT_GPIOF);

	// Place the uDMA channel attributes in a known state. These should already be disabled by default.
	uDMAChannelAttributeDisable(UDMA_CH15_GPIOF,
			UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |
			(UDMA_ATTR_HIGH_PRIORITY |
					UDMA_ATTR_REQMASK));

	uDMAChannelAssign(UDMA_CH15_GPIOF);

	uDMAChannelControlSet(UDMA_CH15_GPIOF | UDMA_PRI_SELECT,
			UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
			UDMA_ARB_4);

	uDMAChannelTransferSet(UDMA_CH15_GPIOF | UDMA_PRI_SELECT,
			UDMA_MODE_AUTO, g_pui8TxBuf, (void *)(SSI0_BASE + SSI_O_DR),
			sizeof(g_pui8TxBuf));

	uDMAChannelEnable(UDMA_CH15_GPIOF);
}

void setupSSI0DMA(void)
{
	// DMA enable for the SSI Module
	SSIDMAEnable(SSI0_BASE, SSI_DMA_RX);

	// Receive channel setup for ping and pong
	uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0RX,
			UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
			UDMA_ATTR_HIGH_PRIORITY |
			UDMA_ATTR_REQMASK);

	uDMAChannelAssign(UDMA_CH10_SSI0RX);

	uDMAChannelControlSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT,
			UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
			UDMA_ARB_1);

	uDMAChannelControlSet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT,
			UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
			UDMA_ARB_1);

	uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT,
			UDMA_MODE_PINGPONG,
			(void *)(SSI0_BASE + SSI_O_DR),
			g_pui8RxPing, sizeof(g_pui8RxPing));

	uDMAChannelTransferSet(UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT,
			UDMA_MODE_PINGPONG,
			(void *)(SSI0_BASE + SSI_O_DR),
			g_pui8RxPong, sizeof(g_pui8RxPong));

	// Enable SSI0 RX DMA
	uDMAChannelEnable(UDMA_CHANNEL_SSI0RX);
	SSIIntEnable(SSI0_BASE, SSI_DMARX);
}

int main(void)
{
	// System clock frequency is 50 MHz => Period is 20ns
	SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

	SysCtlPeripheralClockGating(true);

	IntMasterDisable();

	// Enable DMA
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
	uDMAEnable();
	IntEnable(INT_UDMAERR);
	uDMAControlBaseSet(pui8ControlTable);

	// Configure port F pin 4 as DMA trigger enable
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_GPIOF);
	SysCtlDelay(10000);
	GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4);

	// Setup and configure ADC in RDATAC Mode
	// Do not use DMA yet for SSI0
	setupADCUsingSSI0();

	// Setup SSI0 RX for Ping-Pong acquisition
	setupSSI0DMA();

	// Setup DMA to have DRDYB enable DMA transfer to SSI0 TX port
	setupDMATrigger();

	IntMasterEnable();

	while (1)
	{

	}
}

  • Sridhar Jonnalagadda said:
    i am not receiving the ADC data into the ping pong buffers. In fact the SSI0RXhandler is not getting triggered at all.

    Yours is a sophisticated application (especially so that 24 bit ADC) and I believe you've composed a detailed & well thought post - good job.

    That said - may I lobby for "KISS" - for your consideration of smaller, simpler, more direct waypoints - as you sail toward your end destination?

    Any MCU's µDMA adds several levels of complexity - firm/I do not believe such usage - "from the get-go" (beginning) proves wise.   And your inability to acquire ADC data supports this belief.   (i.e. too much - too soon & far too complex)

    Instead - does it not make sense to (postpone) the complexity of µDMA - and focus upon reading that ADC?   Only when that's well achieved should you, "add complexity."   

    Any 24 bit ADC is a, "monster in thin disguise."   We hope that you've invested in an (official) eval board - these have (absolutely) NO CHANCE of performing correctly w/un: skilled/caring pcb layout and/or "breadboard."   No chance - zero!

    Usually - but not always - vendors producing 24 bit devices also provide (lesser) devices - w/in the same family.   (often these are "fall-out" from automated testing)   Starting w/a lesser device - which has identical interface characteristics (especially signal timing) often proves very wise.

    As to the µDMA - which I'd suggest that you "place on hold" for now - a similar simplification exists.   Instead of feeding the µDMA "live ADC data" (i.e. your end goal) it most always proves wise to (instead) feed it "known, fixed data!"   This eases your testing - makes it "orders of magnitude" easier (and faster) to follow your (now easily recognizable) data as it journeys through the µDMA "nooks/crannies."   

    Lastly - the 7K5Hz (starting) conversion rate seems NOT to require µDMA.    As part of KISS I'd suggest (after the ADC is tamed) that you (first) employ the classic "interrupt based approach" (as you noted) and make that work.   This then may serve as a "baseline" - which if presented clearly to skilled vendor staff here - may enable their comment as to the relative benefit which (may) be achieved via the, "µDMA approach."   Sometimes the best approach is, "No further approach!"   (end may not justify the means)

  • Thank you for the detailed inputs and your suggestions are well taken. I have tried the interrupt based approach and it is working. As you suggested, i have attached the code using interrupts with this post. I hope this might help to get some inputs on possible issues with the DMA approach.

    Since, some of our product offerings need the ADC to run at 15KSPS and 30KSPS, it would help to get DMA approach to work.  I just picked 7.5KSPS as a starting point to explore the DMA approach.

    Best Regards,
    Sridhar

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/tm4c123gh6pm.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_gpio.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/interrupt.h"
    
    //#define TARGET_IS_TM4C123_RA1
    
    #define PF_GPIO_PIN_1 CSB			// Configure as Output
    #define PF_GPIO_PIN_2 RESETB		// Configure as Output
    #define PF_GPIO_PIN_3 SYNCB		// Configure as Output
    #define PF_GPIO_PIN_4 DRDYB		// Configure as Output
    
    #define SYS_CLOCK						(80000000)
    
    /*
     * Using SSI0 on the TIVA launch pad to interface with ADS1255
     * PA5 - SSI0Tx  (master out, MOSI)
     * PA4 - SSI0Rx  (master in, MISO)
     * PA3 - SSI0Fss (chip select)
     * PA2 - SSI0Clk (master clock, SCLK)
     */
    
    // GLOBALS
    volatile uint8_t count = 0;
    uint8_t prevDRDYBLevel = 1;
    volatile uint8_t rdatacflag = 0;
    
    uint8_t Spi_write_data;
    // Cmd and Data to configure ADS1255
    uint8_t ADCWREG[13] = {0x50, 0x0A, 0x02, 0x01, 0x27, 0xD0, 0xE1, 0x00, 0x00, 0x00, 0x08, 0xAC, 0x44};
    
    // Split read registers command into two parts
    // Part 1: Command
    uint8_t ADCCMDREAD[2] = {0x10, 0x0A};
    // Part 2: Actual Read
    uint8_t ADCRREG[11] = {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    void Spi_Read_write_8bit(unsigned char* Pbuff,uint16_t Length,unsigned int uiBase);
    void PortFDRDYBIntHandler();
    
    int main(void)
    {
    	//uint32_t pui32DataRx[4];
    	uint32_t temp = 0;
    
    	// Setting Clocks
    	// There is a default /2 divider at the output of the PLL
    	// Effective System clock frequency is 40 MHz => Period is 25ns
    	SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
    
    	// Enable SSI0 Peripheral
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    
    	// Enable the GPIO Port A, as we are using PA[5:2]
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
    	// Enable the GPIO Port F for interfacing to other ADC Signals
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
    	// Delay to ensure all peripherals are enabled before use
    	SysCtlDelay(3000000);
    
    	// Disable Interrupts for now, Enable later after all the configuration is done.
    	//Initialization should always be done with interrupts disabled
    	IntMasterDisable();
    
    	// Need to control CS#, SYNC#/PWDN# and use interrupt on DRDY# to issue SPI
    	// Commands to read from the ADC
    	// GPIO_PIN_1 => CSB,		Configured as Output
    	// GPIO_PIN_2 => RESETB, 	Configured as Output
    	// GPIO_PIN_3 => SYNCB, 	Configured as Output
    	// GPIO_PIN_4 => DRDYB,		Configured as Input
    	GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    	GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4);
    	//GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD);
    	GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_FALLING_EDGE);
    	GPIOIntRegister(GPIO_PORTF_BASE,PortFDRDYBIntHandler);
    	GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
    	GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
    
    	// Drive CSB, RESETB, SYNCB Logic High
    	// Not required, CSB, RESETB, SYNCB are hard-wired on the board
    	//GPIOPinWrite(GPIO_PORTF_BASE, (GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3), 0x00);
    
    	// SPI/SSI Configuration Code Start
    
    	// Configure pin muxing
    	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    	GPIOPinConfigure(GPIO_PA4_SSI0RX);
    	GPIOPinConfigure(GPIO_PA5_SSI0TX);
    
    	// Configure the GPIO settings for the SSI pins
    	GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);
    
    	// Configure the SSI mode and setup as master
    	temp = SysCtlClockGet();
    	SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 2000000, 8);
    
    	// Enable the SSI Module
    	SSIEnable(SSI0_BASE);
    	SysCtlDelay(3000000);
    
    	// Read any residual data from the SSI Port
    
    	while(SSIDataGetNonBlocking(SSI0_BASE, &temp))
    	{
    		// Nothing to do here
    	}
    
    	// Issue ADC Reset Command
    
    	// Step1: Take CSB Low
    	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00);
    	Spi_write_data = 0xFE;
    	Spi_Read_write_8bit(&Spi_write_data, 1, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step2:  Write ADC Registers
    	Spi_Read_write_8bit(ADCWREG, 13, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step3: Write the command to READ Registers
    	Spi_Read_write_8bit(ADCCMDREAD, 2, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step 4: Read Registers
    	Spi_Read_write_8bit(ADCRREG, 11, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step 5: Poll for DRDYB to go low
    //	prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4);
    //	while(prevDRDYBLevel)
    //	{
    //		prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4);
    //		SysCtlDelay(200);
    //		rdatacflag = 1;
    //	}
    //
    //	Spi_Read_write_8bit(&RDATCREG, 1, SSI0_BASE);
    //	SysCtlDelay(10000);
    
    	//Enable the interrupts
    	IntMasterEnable();
    
    	while (1)
    	{
    
    	}
    }
    
    void Spi_Read_write_8bit(unsigned char* PBuff,uint16_t Length,unsigned int uiBase)
    {
    	uint32_t temp = 0;
    	uint8_t temp_count = 0;
    	while(SSIDataGetNonBlocking(SSI0_BASE, &temp));
    	for(temp_count = 0;temp_count < Length;temp_count++)
    	{
    		SSIDataPut(uiBase, *(PBuff));
    		SSIDataGet(uiBase, &temp);
    		*(PBuff++) = temp;
    	}
    
    }
    
    void PortFDRDYBIntHandler(void) {
    
    	uint8_t RDATCREG =  {0x03};
    
    	// Read ADC Data
    	uint8_t ADCDATA[4] = {0xAA, 0xAA, 0xAA};
    
    	// Clear the interrupt flag
    	GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
    
    	// RDATAC Command needs to be issued just once
    	// Delay not really required
    	if (rdatacflag == 0)
    	{
    		Spi_Read_write_8bit(&RDATCREG, 1, SSI0_BASE);
    		SysCtlDelay(340);
    		rdatacflag = 1;
    	}
    	else
    	{
    		// Read SPI Data
    		Spi_Read_write_8bit(ADCDATA, 3, SSI0_BASE);
    
    	}
    
    }
    
    

  • Hello Sridhar

    I believe the issue is in the function Spi_Read_write_8bit. You are putting and getting data, thus not allowing the DMA channel to get triggered as there is never any data for DMA. Remove the SSIDataGet from the code.

    Regards
    Amit
  • Hi Amit,

    Two quick items:

    • Is that, "removal of SSIDataGet()" very well described w/in MCU manual and/or API?   If not - such should be clearly added.   If it is - better (some) emphasis seems indicated
    • I definitely posted w/in this thread a 2nd time - suggesting that poster chart the time requirements for, "Interrupt based method" - furnish that data to you - so that a (real) comparison of the "benefits" offered by µDMA could be made.   That posting - in its entirety - has disappeared!   Has the moderator an especially "heavy hand?"   I received no notification of any such, "Post Removal Outrage!"   That should be SOP - should it not?   STINKS!

    As µDMA requires so much added time/effort - that complexity should be justified by (some) ready "performance gains" so that the extra time/effort may be (perhaps) justified.   How could such a posting be <DELETED> by forum moderators?   And really - they should inform posters of their HEAVY HAND!

  • Hello cb1

    I don't think so. Anytime a DMA operation and a CPU operation is to a FIFO entry register, a definite race condition is there. This is a logical issue than a design issue.

    Regards
    Amit
  • Hi Amit,

    Don't think we're (really) communicating. My last post's point (again):

    "Is that, "removal of SSIDataGet()" very well described w/in MCU manual and/or API? "

    By, "I don't think so" are you agreeing that the, "removal of SSIDataGet()" does deserve (better) description?

    Poster here "missed that requirement" and I doubt he's alone... (such suggests vendor's description weakness - not logic or design issue.)
  • And - while this µDMA and ADC topic remains active/open:  

    Would not a chart of "typical" performance gains (via µDMA) vs. (more standard) Interrupt based approach (as suggested here - by this poster) prove of great interest & value to (so many) here?   Firm/clients/I believe (surely) so!

    Clearly µDMA offers promise - but is not the, "devil in the details?"   And we have few!   (none?)

    Of course you cannot predict each/every application - but surely "top 3 or so" could be listed - and measured - and then charted.

    Use of the µDMA - whether here or (other vendors) always adds time/effort/complexity.   Should not (some) justification for that investment be presented?   

    "Return on Investment" is widespread & fundamental - that concept proves of great value far beyond its (financial) origins!   Shouldn't it land here - now?

  • Hello cb1,

    No, it is not said explicitly to remove SSIDataGet. But it is a logical method. If the RX path has to be serviced by DMA, the CPU should not perform SSIDataGet.

    Regards
    Amit
  • Indeed it's a logical method - but I guarantee that many (most) will not make that connection. Is it not far safer & caring for your client-users to be warned - clearly - so that they avoid this trap? (note that our poster here appears capable - and he missed this (unmarked) point!

    I see no response (as of yet) to the "Chart of typical execution times" - contrasting µDMA vs. Interrupt driven approach. If µDMA really produces advantages - that fact is (hidden/suppressed) as (now) presented. Side by side comparison chart remedies that "Sales Weakness."
  • Hello cb1,

    The I2C Application Note does show how much time the CPU spends in interrupts v/s when using DMA for data transfer.

    Regards
    Amit
  • Hi Amit,

    Good that - I'll check shortly.

    Again - you're the expert you knew that. I doubt many others - even "regulars" here - have that recollection.

    And - even worse - poster's topic is µDMA AND ADC. Finding such chart in the (unrelated) I2C App Note - has (very near to) ZERO chance of success - does it not?
  • Good that you have the interrupt version working.

    That, as cb1 notes, leaves a couple of questions you should answer for yourself.

    The first is how much of your processor time is spent in the interrupt? Unless that's quite large you are at risk of premature optimization.

    The second is how much of that time could realistically be saved even if DMA took zero time and didn't slow the processor? I have such a bottleneck but it's dominated byprocessing time not interrupt transfer overhead.

    Robert

    Generally DMA is useful at two tasks, reducing latency since there is no need to save context and moving large blocks of data. It's not as useful moving small blocks (setup overhead) and it does slow down the micro by competing for memory access unless the DMA, memory and i/ o have been designed to operate on a different bus than the micro or the memory is faster than the micro.
  • Well said (and liked), Robert.

    Might the "rush" to µDMA - just as you note - prove NOT of universal advantage? Indiscriminate usage - just because a capability "is there" usually provides NO assurance of improvement/benefit.

    When the added effort, delay & uncertainty of performance are considered - µDMA may hinder - not help - the design objectives.

    Friend Robert writes of the (always) unwanted/feared, "Premature Optimization."   Yet - what if NO Optimization results - even worse - performance suffers?   Now - time, effort, & funds have been expended - and are unlikely to be recovered.   Can this (ever) make sense?   

    Magic pills - either medicinal or MCU based - should be employed w/care - and w/real regard to "actual/justifiable" (i.e. measureable) performance results.  This reporter has petitioned for a, "Chart of most typical µDMA performance results - across each of the µDMAs usage areas - so that (some) justification for µDMA is presented.   (or - we can rely upon "blind" (very blind) faith - and hope...)

    Advertising most always paints a (very) rosy picture - which (unfortunately, too often) the product cannot fully/properly deliver.   Caveat Emptor...

  • Hello Amit,

    I am using the function "Spi_Read_write_8bit" during the configuration of the ADC (setupADCUsingSSI0). After the ADC is configured, this function is no longer used. I am using GPIO (PF4) to trigger DMA. Please refer to the function - "uDMAGPIOTrigHandler". This function is called whenever there is a falling edge on PF4 and it is here that i write to the contents of SSI data register using DMA.

    Best Regards,
    Sridhar
  • Hello Robert, Cb1,

    Thanks for your inputs. I will definitely assess if we can use the interrupt based approach for my application. The problem is there is significant data processing to be done and i thought we can possibly use DMA to free up the processor as much as possible. We also use 30KSPS for one of the applications and this is where i suspect DMA might be useful.

    Best Regards,
    Sridhar
  • Sridhar Jonnalagadda said:
    ...30KSPS - this is where i suspect DMA might be useful.

    As Robert & I wrote - your "suspicion" is founded upon hope/desire - there are few (no?) supporting facts to reinforce that suspicion.   (hope, really)

    That tabulation of "typicals" - Interrupt based vs. µDMA based - appears to be badly needed.   We're advised that (some) charts w/timing data appear in an unrelated source document - that's curious - and impossible for most all here to know - or find.

    Proper "Guidance of Posters" would free-up forum experts to create (real) value - such as these comparative timing charts - which greatly serve vendor's client-users.   (or - endless back-forth can (continue) in teasing out the meaning of (always useful) "Does not Work!")

  • Sridhar Jonnalagadda said:
    The problem is there is significant data processing to be done and i thought we can possibly use DMA to free up the processor as much as possible.

    Measure! You have the sample application to do so.  Measure the time in the interrupt transferring and processing. I rather expect from my experience you will find the interrupt overhead to be small. And remember that with DMA there will be time stolen from the CPU for transfers and the setup overhead as well.

    Sridhar Jonnalagadda said:
    We also use 30KSPS for one of the applications and this is where i suspect DMA might be useful.

    That's not dissimilar to an application I have with internal A/Ds running 12 channels @ 10kHz. Two sequencers are used so that's a 20kHz interrupt rate. I've never been tempted to introduce DMA since the time is overwhelmingly dominated by the processing.

    In your case wire speed may be a significant consideration and DMA is of no help for that.

    Robert

  • Above is poster's second "advisement" to replace "suspicion/hope" with real science. Suspicion and assumption have cost many their careers - or severely retarded their project's development.

    Developing the discipline to "always devise performance models - and then measure & contrast" will pay off now and (long) into the future!

  • Hello Sridhar

    And you are sure that the debugger window does not have SSI address space open.

    Regards
    Amit
  • Hi Amit,

    Yes, i can confirm that.

    Best Regards,
    Sridhar
  • Hello Sridhar,

    Can you please share a lite version of your CCS project (zip it and attach it to the forum post). I know you have posted the code, but it would be easier for me to use a project.

    Regards
    Amit
  • Hi Amit,

    I have attached the project.


    Best Regards,

    Sridhar


    SPI_Ping_Pong.zip

  • #include <stdint.h>
    #include <stdbool.h>
    #include "inc/tm4c123gh6pm.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_gpio.h"
    #include "inc/hw_uart.h"
    #include "inc/hw_ssi.h"
    #include "driverlib/fpu.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/gpio.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/udma.h"
    
    // GLOBALS
    uint8_t prevDRDYBLevel;
    
    uint8_t Spi_write_data;
    
    // Cmd and Data to configure ADS1255
    uint8_t ADCWREG[13] = {0x50, 0x0A, 0x02, 0x01, 0x27, 0xD0, 0xE1, 0x00, 0x00, 0x00, 0x08, 0xAC, 0x44};
    
    // Split read registers command into two parts
    
    // Part 1: Command
    uint8_t ADCCMDREAD[2] = {0x10, 0x0A};
    // Part 2: Actual Read
    uint8_t ADCRREG[11] = {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    // Read Data Continuously
    uint8_t RDATCREG =  {0x03};
    
    // Ping-Pong Buffer definitions
    #define UART_TXBUF_SIZE        256
    #define  SPI_RXBUF_SIZE        8
    static uint8_t g_pui8TxBuf[3] = {0xAA, 0xAA, 0xAA};
    static uint32_t g_pui8RxPing[SPI_RXBUF_SIZE];
    static uint32_t g_pui8RxPong[SPI_RXBUF_SIZE];
    
    // Define errors counters
    static uint32_t g_ui32BadISR = 0;
    static uint32_t g_ui32DMAErrCount = 0;
    
    // Define transfer counter
    static uint32_t g_ui32MemXferCount = 0;
    
    // Transfer counters
    static uint32_t g_ui32RxPingCount = 0;
    static uint32_t g_ui32RxPongCount = 0;
    
    // The control table used by the uDMA controller.  This table must be aligned to a 1024 byte boundary.
    #pragma DATA_ALIGN(pui8ControlTable, 1024)
    uint8_t pui8ControlTable[1024];
    
    // Library error routine
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    // uDMA transfer error handler
    void uDMAErrorHandler(void)
    {
    	uint32_t ui32Status;
    
    	// Check for uDMA error bit
    	ui32Status = uDMAErrorStatusGet();
    
    	// If there is a uDMA error, then clear the error and increment the error counter.
    	if(ui32Status)
    	{
    		uDMAErrorStatusClear();
    		g_ui32DMAErrCount++;
    	}
    }
    
    // PF4 (DRDYB) uDMA Trigger interrupt handler
    void uDMAGPIOTrigHandler(void)
    {
    	uint32_t ui32Mode;
    
    	GPIOIntClear(GPIO_PORTF_BASE, GPIO_INT_DMA);
    
    	// Check for the primary control structure to indicate complete.
    	ui32Mode = uDMAChannelModeGet(UDMA_CH15_GPIOF);
    
    	if(ui32Mode == UDMA_MODE_STOP)
    	{
    		// Increment the count of completed transfers.
    		g_ui32MemXferCount++;
    
    		// Configure it for another transfer.
    		uDMAChannelTransferSet(UDMA_CH15_GPIOF, UDMA_MODE_AUTO,
    				g_pui8TxBuf, (void *)(SSI0_BASE + SSI_O_DR),
    				sizeof(g_pui8TxBuf));
    
    		// Initiate another transfer.
    		uDMAChannelEnable(UDMA_CH15_GPIOF);
    	}
    
    	// If the channel is not stopped, then something is wrong.
    	else
    	{
    		g_ui32BadISR++;
    	}
    }
    
    
    // SSI0 Receive Interrupt Handler
    void SSI0RXHandler(void)
    {
    	uint32_t ui32Status;
    	uint32_t ui32Mode;
    
    	ui32Status = SSIIntStatus(SSI0_BASE, 1);
    
    	SSIIntClear(SSI0_BASE, ui32Status);
    
    	ui32Mode = uDMAChannelModeGet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT);
    
    	if(ui32Mode == UDMA_MODE_STOP)
    	{
    		g_ui32RxPingCount++;
    
    		uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT,
    				UDMA_MODE_PINGPONG,
    				(void *)(SSI0_BASE + SSI_O_DR),
    				g_pui8RxPing, sizeof(g_pui8RxPing));
    	}
    
    	ui32Mode = uDMAChannelModeGet(UDMA_CH10_SSI0RX | UDMA_ALT_SELECT);
    
    	if(ui32Mode == UDMA_MODE_STOP)
    	{
    		g_ui32RxPongCount++;
    
    		uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_ALT_SELECT,
    				UDMA_MODE_PINGPONG,
    				(void *)(SSI0_BASE + SSI_O_DR),
    				g_pui8RxPong, sizeof(g_pui8RxPong));
    	}
    
    	uDMAChannelAssign(UDMA_CH10_SSI0RX);
    	SSIDMAEnable(SSI0_BASE, SSI_DMA_RX);
    	uDMAChannelEnable(UDMA_CH10_SSI0RX);
    
    }
    
    void Spi_Read_write_8bit(unsigned char* PBuff,uint16_t Length,unsigned int uiBase)
    {
    	uint32_t temp = 0;
    	uint8_t temp_count = 0;
    	while(SSIDataGetNonBlocking(SSI0_BASE, &temp));
    	for(temp_count = 0;temp_count < Length;temp_count++)
    	{
    		SSIDataPut(uiBase, *(PBuff));
    		SSIDataGet(uiBase, &temp);
    		*(PBuff++) = temp;
    	}
    
    }
    
    void setupADCUsingSSI0(void)
    {
    	uint32_t temp;
    	// SPI/SSI Configuration Code Start
    
    	// Enable SSI0 Peripheral
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_SSI0);
    
    	// Enable the GPIO Port A, as we are using PA[5:2]
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_GPIOA);
    
    	SysCtlDelay(10000);
    
    	// Configure pin muxing
    	GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    	GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    	GPIOPinConfigure(GPIO_PA4_SSI0RX);
    	GPIOPinConfigure(GPIO_PA5_SSI0TX);
    
    	// Configure the GPIO settings for the SSI pins
    	GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);
    
    	// Configure the SSI mode and setup as master
    	SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_1, SSI_MODE_MASTER, 2000000, 8);
    
    	// Enable the SSI Module
    	SSIEnable(SSI0_BASE);
    
    	SysCtlDelay(3000000);
    
    	// Read any residual data from the SSI Port
    
    	while(SSIDataGetNonBlocking(SSI0_BASE, &temp))
    	{
    		// Nothing to do here
    	}
    
    	// Issue ADC Reset Command
    
    	// Step1: Take CSB Low
    	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00);
    	Spi_write_data = 0xFE;
    	Spi_Read_write_8bit(&Spi_write_data, 1, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step2:  Write ADC Registers
    	Spi_Read_write_8bit(ADCWREG, 13, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step3: Write the command to READ Registers
    	Spi_Read_write_8bit(ADCCMDREAD, 2, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	// Step 4: Read Registers
    	Spi_Read_write_8bit(ADCRREG, 11, SSI0_BASE);
    	SysCtlDelay(10000);
    
    	//Step 5: Try to catch the DRDBYB falling edge without using interrupts
    
    	while(!GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4));
    
    	prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4);
    
    	while(prevDRDYBLevel)
    	{
    		prevDRDYBLevel = GPIOPinRead(GPIO_PORTF_BASE,GPIO_PIN_4);
    		SysCtlDelay(200);
    	}
    
    	Spi_Read_write_8bit(&RDATCREG, 1, SSI0_BASE);
    	SysCtlDelay(10000);
    
    }
    
    void setupDMATrigger(void)
    {
    
    	// Note DRDYB is the uDMA Trigger, PF4
    	// Configure and enable the GPIOF with DMA
    	GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4);
    	GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4, GPIO_FALLING_EDGE);
    	GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU);
    	GPIOIntClear(GPIO_PORTF_BASE, GPIO_PIN_4);
    
    	GPIODMATriggerEnable(GPIO_PORTF_BASE, GPIO_PIN_4);
    
    	GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_DMA);
    
    	IntEnable(INT_GPIOF);
    
    	// Place the uDMA channel attributes in a known state. These should already be disabled by default.
    	uDMAChannelAttributeDisable(UDMA_CH15_GPIOF,
    			UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |
    			(UDMA_ATTR_HIGH_PRIORITY |
    					UDMA_ATTR_REQMASK));
    
    	uDMAChannelAssign(UDMA_CH15_GPIOF);
    
    	uDMAChannelControlSet(UDMA_CH15_GPIOF | UDMA_PRI_SELECT,
    			UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
    			UDMA_ARB_4);
    
    	uDMAChannelTransferSet(UDMA_CH15_GPIOF | UDMA_PRI_SELECT,
    			UDMA_MODE_AUTO, g_pui8TxBuf, (void *)(SSI0_BASE + SSI_O_DR),
    			sizeof(g_pui8TxBuf));
    
    	uDMAChannelEnable(UDMA_CH15_GPIOF);
    }
    
    void setupSSI0DMA(void)
    {
    	// DMA enable for the SSI Module
    	SSIDMAEnable(SSI0_BASE, SSI_DMA_RX);
    
    	// Receive channel setup for ping and pong
    	uDMAChannelAttributeDisable(UDMA_CH10_SSI0RX,
    			UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
    			UDMA_ATTR_HIGH_PRIORITY |
    			UDMA_ATTR_REQMASK);
    
    	uDMAChannelAssign(UDMA_CH10_SSI0RX);
    
    	uDMAChannelAssign(UDMA_CH11_SSI0TX);
    	uDMAChannelEnable(UDMA_CH11_SSI0TX);
    
    	uDMAChannelControlSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT,
    			UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 |
    			UDMA_ARB_1);
    
    	uDMAChannelControlSet(UDMA_CH10_SSI0RX | UDMA_ALT_SELECT,
    			UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 |
    			UDMA_ARB_1);
    
    	uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT,
    			UDMA_MODE_PINGPONG,
    			(void *)(SSI0_BASE + SSI_O_DR),
    			g_pui8RxPing, sizeof(g_pui8RxPing));
    
    	uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_ALT_SELECT,
    			UDMA_MODE_PINGPONG,
    			(void *)(SSI0_BASE + SSI_O_DR),
    			g_pui8RxPong, sizeof(g_pui8RxPong));
    
    	// Enable SSI0 RX DMA
    	uDMAChannelEnable(UDMA_CH10_SSI0RX);
    	SSIIntEnable(SSI0_BASE, SSI_DMARX);
    	IntEnable(INT_SSI0);
    }
    
    int main(void)
    {
    	// System clock frequency is 50 MHz
    	SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
    
    	SysCtlPeripheralClockGating(true);
    
    	IntMasterDisable();
    
    	// Enable DMA
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    	SysCtlDelay(100);
    	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
    	uDMAEnable();
    	IntEnable(INT_UDMAERR);
    	uDMAControlBaseSet(pui8ControlTable);
    
    	// Configure port F pin 4 as DMA trigger enable
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    	SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_GPIOF);
    	SysCtlDelay(100);
    	GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4);
    	HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= GPIO_PIN_4;
    
    
    	// Setup and configure ADC in RDATAC Mode
    	// Do not use DMA yet for SSI0
    	setupADCUsingSSI0();
    
    	// Setup SSI0 RX for Ping-Pong acquisition
    	setupSSI0DMA();
    
    	// Setup DMA to have DRDYB enable DMA transfer to SSI0 TX port
    	setupDMATrigger();
    
    	IntMasterEnable();
    
    	while (1)
    	{
    
    	}
    }
    
    
    Hello Sridhar,

    The issue was the missing Interrupt Enable for SSI0.

    IntEnable(INT_SSI0);

    Also i had to make some changes to the code which is attached. Overall, I strongly believe the code needs to be better structured and commented.

    Regards

    Amit

  • Hi Amit,

    W/in posters very first posting - I quote, "In fact the SSI0RXhandler is not getting triggered at all."

    And you resolve (good that) and report, "missing Interrupt Enable for SSI0."

    We back-benchers are left to wonder - "somewhere w/in the cavernous µDMA code block - was that SSI0 interrupt deliberately disabled?"  Poster seems aware & competent - thus the "simplicity" of the solution (not your teasing it out) causes concern.   (he made multiple (other) use of  "IntEnable()" thus this "miss" of  "INT_SSI0" proves curious.)    Might this "INT_SSIx" have been (missed) w/in (published) code examples?   Thank you...

  • Hello cb1

    It was never enabled in the first place.

    Regards
    Amit
  • Hi Amit,

    My net is "in/out" I added to my final para. Poster DID employ, "IntEnable()" w/in several other places w/in his (long) code. His code was modeled (one suspects) from existing examples - is it possible "IntEnable()" is not (properly) described/modeled w/in SSI code examples?

    Note that he identified that, "lack of interrupt" w/in his very first post!   So he knew the issue.   His miss of "IntEnable()" proves curious - especially as he used that function several times (although in different modes) - does it not?

    Should not a forum's goal be to resolve related MCU tech issues - and attempt to identify "conditions" which may contribute to such issues?   (my point)

  • Hello cb1,

    I checked the uDMA examples in TivaWare and it does enable the Interrupt in the NVIC for the peripheral on which the DMA Done Interrupt will be triggered.

    Regards
    Amit
  • Hi Amit,

    That's quite a bit of "vendor" tech speak - is it not? Does, "IntEnable()" in that very form - appear w/in those examples?

    If it does - poster simply missed. (his VERY bad!)   If "IntEnable()" does not specifically appear - might others succumb to this same "trap?"

  • Hello cb1

    Yes, it does appear as ROM_IntEnable(INT_UART1) for UART DMA channel and ROM_IntEnable(INT_UDMA) for UDMA software channels

    Regards
    Amit
  • Hi Amit,

    Thank you - that level of detail should prove helpful to many.  (especially moi - earlier note of NVIC & DMA_DONE - not so much!)

    Again - my interest usually is, "How & Why" posters become derailed - and can anything be done to "LESSEN" that occurrence. The clear fact that poster - from the "get-go" - identified that failed SSI0 Interrupt - yet could not resolve - caught firm's/my interest.

    It would prove useful to many if poster - upon reading this - would describe how he, "came so close" yet missed that, "IntEnable()."

  • Hi Amit,

    Thanks for the support. I have checked the code and confirm that it is working now. Will take your feedback and restructure.

    Hi Cb1, Robert, Thanks for the detailed inputs and insight.

    Best Regards,
    Sridhar
  • Glad that you succeeded - you were "so" close.

    You would greatly "Serve the forum" by measuring the execution time (perhaps the time spent in converting 8 channels under Sequence 0) "both" via this µDMA approach and ALSO via your (original) Interrupt approach.

    Doing so for all 3 of your specified conversion rates (7K5 - 30K - if memory serves) would prove useful to MCU Users (yourself, Robert, cb1, others) and (even) to this vendor. Poster Robert & I share the belief that "not always" will the µDMA justify its overhead, complexity and (unwanted) impact upon all other aspects of MCU operation. (i.e. µDMA extracts some execution price (slows) - there is NO Free lunch!)
  • Hello Sridhar,

    Especially the DMA transfer structures can be better optimized. Use a Transfer Size in multiples of 3 to make sure the right amount of data is read into each of the Ping Pong buffers. Else it may be split across the two buffers.

    Regards
    Amit
  • Hi Amit,

    The transfer size is set to 1023 - the size of g_pui8RxPing buffer..

    uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT,
    UDMA_MODE_PINGPONG,
    (void *)(SSI0_BASE + SSI_O_DR),
    g_pui8RxPing, sizeof(g_pui8RxPing));

    Hello Cb1,

    I shall definitely attempt to do so. Can you please clarify a bit on the required measurements just so that i can understand them better? My current setup reads data from a single channel of an external ADC (ADS1255). I did not catch - "perhaps the time spent in converting 8 channels under Sequence 0"..

    Best Regards,
    Sridhar
  • Hello Sridhar,

    If the value programmed for the transfer size is 0x3FF it would mean 1024 transfer which is not a multiple of 3. If the value programmed is 0x3FE, then it should be fine

    Regards
    Amit
  • Hi Amit, Sure I shall make sure. Thank you!

    Best Regards,
    Sridhar
  • Sridhar Jonnalagadda said:
    reads data from a single channel of an external ADC (ADS1255). I did not catch - "perhaps the time spent in converting 8 channels under Sequence 0"... 

    Indeed - @ 36 posts (& counting) I must plead (temporary) loss of focus upon your use of an "external ADC."   (My bad.)

    What would prove useful to you - and to those (also) considering the time/effort demanded by µDMA - would be your measurement & charting of the time required by (your original) "Interrupt based approach" vs. "µDMA based approach" - for each of the 3 conversion rates you mentioned.   (7K5SPS thru 30KSPS (from memory)).

    Both poster Robert & I (doubt) that much benefit will accrue (via µDMA) under the conditions described w/in this thread...