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.

ADC uDMA USB problem

Other Parts Discussed in Thread: EK-TM4C1294XL, TM4C1294NCPDT

Hi,

I am using my Stellaris LaunchPad to sample the internal temp. sensor and send this data via USB to a host PC.

To do so I use SS1 from ADC0 which is set to continuous mode at 1 MS/s. uDMA is configured to copy data from SS1 fifo directly to the USB tx buffer (I am using the usb code found in usb_dev_bulk). I also tried to copy the data first into extra buffers and thus configured uDMA to operate in Ping-Pong mode.

Furthermore, I configured the systick timer to count every us.


Basically the code is operating. However, I am facing some problems. When I measure the time spend between two ADC interrupts (uDMA transfer complete) I would expect it to be something like

number_of_samples_to_copy * 1 us/sample


However, sometimes I am getting durations which are far below that. For example, if I setup uDMA to copy 64 samples, the expected time would be above 64us but sometimes I measure something like 30us. I measure the time by counting the number of interrupts for a while and than divide the systick counter by this number.
This issue seems to be worth if no USB host is connected.


Furthermore, I am only getting bulk transfer rates around 700kBytes/s but I would require (if I only use 8bit per sample) 1MS/s * 8bit/S = 1Mbyte


As I am pretty new to Stellaris and USB I apologize if this is a stupid question.

Cheers,

Julian

void sysTickIntHandler(void)
{
	us_counter++;
}

int main(void)
{

	FPULazyStackingEnable();
	FPUEnable();

	SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
	GPIOPinTypeUSBAnalog(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);


	// Initialize the transmit and receive buffers.
	//
	USBBufferInit(&g_sTxBuffer);
	USBBufferInit(&g_sRxBuffer);

	//
	// Set the USB stack mode to Device mode with VBUS monitoring.
	//
	USBStackModeSet(0, eUSBModeForceDevice, 0);

	//
	// Pass our device information to the USB library and place the device
	// on the bus.
	//
	void* retval = USBDBulkInit(0, &g_sBulkDevice);

	ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_ALWAYS, 0);

	ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_TS);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_TS);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_TS);
	ADCSequenceStepConfigure(ADC0_BASE, 1, 3, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);

	uint32_t i = 0;
	for(i = 0;i<1024;i++)
	{
	   currTempVals[i] = 27;//(1475 - ((2475 * pingBuffer[i])) / 4096)/10;
	}

	initDMA();

	ADCSequenceEnable(ADC0_BASE, 1);
	IntEnable(INT_ADC0SS1);
	//ADCIntEnable(ADC0_BASE,1);

	IntEnable(INT_UDMAERR);

	SysTickPeriodSet(50); //1us
	SysTickIntRegister(sysTickIntHandler);
	SysTickIntEnable();
	SysTickEnable();

	uint32_t startProcess=0;

	while(1)
	{

		if(pingBufferReady==1)
		{
			startProcess = us_counter;

			if(usbConnected==1)
			{
				USBBufferDataWritten(&g_sTxBuffer, 64);
			}

			processingTime = us_counter-startProcess;
			pingBufferReady = 0;
			g_ui32DataProcessed++;

		}

		if(pongBufferReady==1)
		{
			if(usbConnected==1)
			{
				USBBufferDataWritten(&g_sTxBuffer, 64);
			}

			pongBufferReady = 0;
		}

	}
}


#pragma DATA_ALIGN(dmaControlData,1024);
uint8_t dmaControlData[1024];

void initDMA()
{


	uDMAEnable();
	uDMAControlBaseSet(dmaControlData);


	uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC1,
	UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
	UDMA_ATTR_HIGH_PRIORITY |
	UDMA_ATTR_REQMASK);

	uDMAChannelControlSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
	UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
	UDMA_ARB_4);

	uDMAChannelControlSet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT,
	UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
	UDMA_ARB_4);

	uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
	UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO1),
	g_pui8USBTxBuffer, 64);

	uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT,
	UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO1 ),
	g_pui8USBTxBuffer, 64);

	//uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC1,UDMA_ATTR_USEBURST|UDMA_ATTR_REQMASK);

	uDMAChannelEnable(UDMA_CHANNEL_ADC1);
}

void ADC0IntHandler(void)
{

	ADCIntClear(ADC0_BASE, 1);

	uint32_t ui32Mode;
	g_ui32IntCount++;

	ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT);
	if(ui32Mode == UDMA_MODE_STOP)
	{

		if(pongBufferReady == 1)
		{
			//Error pongbuffer not processed yet!
			errorCounter++;
		}

		pingBufferReady = 1;
		g_ui32RxPingCount++;
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
		UDMA_MODE_PINGPONG,
		(void *)(ADC0_BASE + ADC_O_SSFIFO1),
		g_pui8USBTxBuffer, 64);
	}

	ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT);

	if(ui32Mode == UDMA_MODE_STOP)
	{

		if(pingBufferReady == 1)
		{
			//Error pingbuffer not processed yet!
			errorCounter++;
		}


		pongBufferReady = 1;
		g_ui32RxPongCount++;
		uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT,
		UDMA_MODE_PINGPONG,
		(void *)(ADC0_BASE + ADC_O_SSFIFO1),
		g_pui8USBTxBuffer, 64);

	}

	//uDMAChannelEnable(UDMA_CHANNEL_ADC1);

}

  • Hello Julian,

    As per the code the processing time computed is around the USB Data Transfer and not for the Ping to Pong Interrupt time. In other words, you would need to mark the start at pingBufferReady=1 condition in while 1 loop and end time at pongBufferReady==1 and then check the time for data transfer and conversion.

    Regards

    Amit

  • Hi Amit,

    Thank you for your quick reply!

    I am not sure if i get the point. Once 64 ADC conversions are ready the ADC is calling the uDMA to copy those samples to a plain buffer in memory which is also used as USB tx buffer. Once the data is copied I will receive an interrupt in my ADC interrupt handler. These interrupts I am counting. So why is the calculation us_counter/g_ui32IntCount related to the USB transfer speed?


    Cheers,

    Julian

  • Hello Julian,

    No, When the 4 conversions are completed the uDMA request will be called as the IE bit is set for the 4ths sample conversion. It would take 16 uDMABurst Request for the uDMA to transfer 64 conversions.

    What I am trying to make a point here is that count the us_counter in the ADC Interrupt Handler from one ping to another ping interrupt.

    Regards

    Amit

  • Hi Amit,

    sorry for asking you again but I am still not getting it. In my opinion if I configure the uDMA like so

    uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO1),
    g_pui8USBTxBuffer, 64);


    I think I should get an ADC interrupt every time the uDMA finished a transfer of 64 samples (One time the samples went to ping buffer the other time to pong buffer). Therefore, counting the inrrupts like I do and compare them to us_counter should give me the desired result?

    Cheers,

    Julian

  • Hello Julian,

    Yes, that is correct. I was referring to the previous post where you mentioned that ADC will transfer data once 64 conversion are done. The ADC will transfer data when 4 conversions are ready and will give an interrupt when 64 transfers are done, then switch to the other buffer,

    Regards

    Amit

  • Example code for the Tiva Launchpad (EK-TM4C1294XL, TM4C1294NCPDT) here: http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/331374/1155144.aspx

    It uses ethernet for 32 Mbps, 1 MS/s at 16 bits/sample (the ADC actually gives 12 bits/sample).

    I realize you're using the Stellaris Launchpad EK-LM4F120XL with LM4F120H5QR but in that thread Amit gave really good information about uDMA bandwidth over the AHB, and there's example code as well. Might be useful.

  • Hi,

    thank you both for your help!

    I did a few more measurements and tried to incorporate as much from the code David pointed at as possible.
    Furthermore I am now measuring the time between ping and pong as Amit suggested but still the result is the same. The time needed is always below 64us. When no USB host is connected it is between 57-60us and if an USB host is connected it can be as low as 40us. Moreover, the USB speed is still below 800kBytes/s.

    I initialize ADC and uDMA as follows:

    void initDMA()
    {
    	ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_ALWAYS, 0);
    
    	ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_TS);
    	ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_TS);
    	ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_TS);
    	ADCSequenceStepConfigure(ADC0_BASE, 1, 3, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);
    	ADCSequenceEnable(ADC0_BASE, 1);
    
    	uDMAEnable();
    	uDMAControlBaseSet(dmaControlData);
    	ADCSequenceDMAEnable(ADC0_BASE, 1);
    
    	uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC1,
    	UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
    	UDMA_ATTR_HIGH_PRIORITY |
    	UDMA_ATTR_REQMASK);
    
    	uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC1,UDMA_ATTR_USEBURST);
    
    	uDMAChannelControlSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    	UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
    	UDMA_ARB_4);
    
    	uDMAChannelControlSet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT,
    	UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
    	UDMA_ARB_4);
    
    	uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    	UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO1),
    	g_pui8USBTxBuffer, 64);
    
    	uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT,
    	UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO1 ),
    	g_pui8USBTxBuffer, 64);
    
    	IntEnable(INT_ADC0SS1);
    	ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS1);
    	uDMAChannelEnable(UDMA_CHANNEL_ADC1);
    }

    In the ADC interrupt handler:

    void ADC0IntHandler(void)
    {
    
    	uint32_t ui32Mode;
    
    	ui32ADCIntStatus = ADCIntStatus(ADC0_BASE,1,true);
    
    	ADCIntClearEx(ADC0_BASE,ui32ADCIntStatus);
    
    	g_ui32IntCount++;
    
    	if(ui32ADCIntStatus & ADC_INT_DMA_SS1) {
    		ui32MaskIntCount++;
    	}
    
    	ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT);
    	if(ui32Mode == UDMA_MODE_STOP)
    	{
    
    		if(pongBufferReady == 1)
    		{
    			//Error pongbuffer not processed yet!
    			errorCounter++;
    		}
    
    		pingBufferReady = 1;
    		g_ui32RxPingCount++;
    		uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    		UDMA_MODE_PINGPONG,
    		(void *)(ADC0_BASE + ADC_O_SSFIFO1),
    		g_pui8USBTxBuffer, 64);
    	}
    
    	ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT);
    
    	if(ui32Mode == UDMA_MODE_STOP)
    	{
    
    		if(pingBufferReady == 1)
    		{
    			//Error pingbuffer not processed yet!
    			errorCounter++;
    		}
    
    
    		pongBufferReady = 1;
    		g_ui32RxPongCount++;
    		uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT,
    		UDMA_MODE_PINGPONG,
    		(void *)(ADC0_BASE + ADC_O_SSFIFO1),
    		g_pui8USBTxBuffer, 64);
    
    	}
    
    }

    Here, ui32MaskIntCount is never increased and the return value of ADCIntStatus(..) is always 0.

    Cheers,

    Julian

  • Hello Julian,

    Would you kindly release your code in whole project. I need to trace it in Code composer studio.

    Regards

    Auson