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.

TM4C129XNCZAD: DK-TM4C129 uDMA and ADC settings

Part Number: TM4C129XNCZAD


Hello!

I have spent queiet a lot of time to setup uDMA to work with ADC0 on my DK-TM4C129x. Examples on the forum didn't work complitely for me, so I decided to share my expirience. Hope it will be helpfull for someone.

My goals were:

  1. Setup ADC0 to make five measurments (ADCSequence0) with a given period (conversions are triggered by timer);
  2. Setup uDMA to transfer ADC results to two buffers (ping-pong mode). Buffers are twodimensional and can hold 30 sets of measurments. Like array a[30][5]:
    Ch0 result Ch1 result Ch2 result Ch3 result Ch4 result
    conversion #1 1 2 3 4 5
    conversion #2 1 2 3 4 5
    ... .. .. .. .. ..
    conversion #30 1 2 3 4 5
  3. Make an uDMA interrupt when one of the buffers is full and then process data from this buffer. Meanwhile the second buffer is filling and when it is full new interrupt occur and I process new data. 

Parts of code which I used to do that: 

  1. Some defines: 
    #define NUMBER_OF_STEPS		5		//Number of conversions in ADC0 Sequence 0
    #define NUMBER_OF_SETS			30		//Number of sets of conversions which are stored in buffer
    
    //Buffers for storing ADC convertion results
    int g_ui8RxBufA[NUMBER_OF_SETS][NUMBER_OF_STEPS], g_ui8RxBufB[NUMBER_OF_SETS][NUMBER_OF_STEPS];

  2. ADC settings: 
    //Power-up ADC0
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    
    //Configure the ADC to use PLL at 480 MHz divided by 24 to get an ADC clock of 20 MHz.
    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 6);
    
    //GPIO port E and K needs to be enabled
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
    
    //Select the analog ADC function for these pins.
    GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6);
    GPIOPinTypeADC(GPIO_PORTK_BASE, GPIO_PIN_0);
    
    //Enable sample sequence 0 with a processor signal trigger.
    ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    
    // Configure steps on sequence.  Sample channels in single-ended mode (default) and configure the interrupt flag after last conversion
    ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0 | ADC_CTL_SHOLD_4);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1 | ADC_CTL_SHOLD_4);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH16 | ADC_CTL_SHOLD_4);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH20 | ADC_CTL_SHOLD_4);
    ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_TS | ADC_CTL_SHOLD_4 | ADC_CTL_IE | ADC_CTL_END);
    
    //Configure the hardware oversampling factor of the ADC.
    ADCHardwareOversampleConfigure(ADC0_BASE, 2);
    
    //Enable ADC Sequence 0 Interrupt on the processor (NVIC).
    IntEnable(INT_ADC0SS0);
    
    //Enables DMA for sample sequencers.
    ADCSequenceDMAEnable(ADC0_BASE, 0);
    
    // Enables a sample sequence interrupt.
    ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    
    // Since sample sequence 0 is now configured, it must be enabled.
    ADCSequenceEnable(ADC0_BASE, 0);
    
    // Clear the interrupt status flag.  This is done to make sure the interrupt flag is cleared before we sample.
    ADCIntClear(ADC0_BASE, 0);
    
    // Trigger the ADC conversion.
    ADCProcessorTrigger(ADC0_BASE, 0);

  3. Timer settings:
    //Configure Timer 1
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    
    TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);
    TimerLoadSet(TIMER1_BASE, TIMER_A, ui32SysClock / 25000);
    IntEnable(INT_TIMER1A);
    TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    
    TimerEnable(TIMER1_BASE, TIMER_A);

  4. Timer interrupt handler:
    void Timer1AIntHandler(void)
    {
    	//Clear Timer 1 interrupt flag
    	TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    
    	//Trigger ADC conversion
    	ADCProcessorTrigger(ADC0_BASE, 0);
    }

  5. uDMA settings:
    //Power-up DMA
     SysCtlPeripheralReset(SYSCTL_PERIPH_UDMA);
     SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    
    //Enables the uDMA controller for use.
    uDMAEnable();
    
    uDMAControlBaseSet(pui8ControlTable);
    
    // Put the attributes in a known state.  These should already be disabled by default.
    uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALL);
    
    // Set the USEBURST attribute. This is somewhat more effecient bus usage than the default which allows single or burst transfers.
    uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0, UDMA_ATTR_USEBURST);
    
    
    // Configure the control parameters for the primary control structure for
    uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    						  UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_NEXT_USEBURST |
    						  UDMA_ARB_256 | UDMA_DST_INC_32);
    
    uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    						  UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_NEXT_USEBURST |
    						  UDMA_ARB_256 | UDMA_DST_INC_32);
    
    
    // Set up the transfer parameters for the primary control structure.  The mode is set to ping-pong
    uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG,
    						   (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    						   g_ui8RxBufA, NUMBER_OF_SETS * NUMBER_OF_STEPS);
    
    uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG,
    						   (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    						   g_ui8RxBufB, NUMBER_OF_SETS * NUMBER_OF_STEPS);
    
    ROM_uDMAChannelEnable(UDMA_CHANNEL_ADC0);

  6. uDMA interrupt handler:
    void ADC0S0IntHandler(void)
    {
    	uint16_t i = 0, j = 0;
    	uint32_t intStatusEx = 0;
    	uint32_t priChMode = 0, altChMode = 0;
    
    	//Get interrupt status
    	intStatusEx = ADCIntStatusEx(ADC0_BASE, 0);
    	
    	//Get mode of primary DMA channel
    	priChMode = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT);
    	
    	//Get mode of alternative DMA channel
    	altChMode = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);
    	
    	//Clear interrupt flag
    	ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0);
    
    	//Clear and re-init primary DMA buffer if it's full
    	if (priChMode == 0)
    	{
    		for (i = 0; i < NUMBER_OF_STEPS; i++)
    		{
    			for (j = 0; j < NUMBER_OF_SETS; j++)
    			{
    				g_ui8RxBufA[i][j] = 0;
    			}
    		}
    	    uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG,
    	                               (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    								   g_ui8RxBufA, NUMBER_OF_SETS * NUMBER_OF_STEPS);
    	}
    
    	//Clear and re-init alternative DMA buffer if it's full
    	if (altChMode == 0)
    	{
    		for (i = 0; i < NUMBER_OF_STEPS; i++)
    		{
    			for (j = 0; j < NUMBER_OF_SETS; j++)
    			{
    				g_ui8RxBufB[i][j] = 0;
    			}
    		}
    		uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG,
    				                               (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    											   g_ui8RxBufB, NUMBER_OF_SETS * NUMBER_OF_STEPS);
    	}
    }

  7. And of course do not forget to register your interrupt handler in the vector table.

P.S. And here is some weird problems which I encountered during debugging my code (I can't explain them, but maybe it will somehow help someone):

  1. First buffer was filling wrong. I have 5 steps in ADCSequence0 and in my buffer instead of results like [1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 ...] I got results like [1 2 3 4 5 5 5 5 5 5 5 5 5 5 ..]. So the filling of DMA buffer wasn't stopped after end of ADC0 Sequence0 FIFO. This way first buffer was filling very quickly (within period of one ADC conversion). But then second buffer filled properly ([1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 ...]).
  2. Only second (alternative) buffer was filling. Function uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) for primary buffer returns "0" (Stop mode) and calling of uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0),g_ui8RxBufA, 30) didn't help.
  3. It was kind of confusing to set the right cause for interrupt. Because I needed an uDMA interrupt, but it's neccessary to enable it in ADC0 registers.