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.

MSP432: Transfer more than 1024 ADC14 samples via DMA



Hi all,

I want to read out a peripheral device that provides ~10'000 analog samples. For that, I plan to copy the samples via DMA to the memory, while the CPU can stay in LPM. I configured everything like this:

	    /* Setting reference voltage to 2.5  and enabling reference */
	    MAP_REF_A_setReferenceVoltage(REF_A_VREF2_5V);
	    MAP_REF_A_enableReferenceVoltage();

	    /* Initializing ADC (MCLK/1/1) */
	    MAP_ADC14_enableModule();
	    MAP_ADC14_initModule(ADC_CLOCKSOURCE_SMCLK, ADC_PREDIVIDER_1, ADC_DIVIDER_1, 0);

	    /* Configuring GPIOs for Analog In */
	    MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_ANALOG, GPIO_PIN_ANALOG, GPIO_TERTIARY_MODULE_FUNCTION);

	    /* Configuring ADC Memory (ADC_MEM0 - ADC_MEM7 (A0 - A1)  with no repeat)
	     * with internal 2.5v reference */
	    ADC14_configureSingleSampleMode(ADC_MEM11, true);
	    ADC14_configureConversionMemory(ADC_MEM11,
	            ADC_VREFPOS_INTBUF_VREFNEG_VSS,
	            ADC_INPUT_A11, false);

	    /* Configuring DMA module */
	    MAP_DMA_enableModule();
	    MAP_DMA_setControlBase(controlTable);

	    /*
	     * Primary DMA Channel, ADC12C
	     * Size = 16bits
	     * Source Increment = 16bits
	     * Destination Increment = 16bits
	     * Arbitration = 1 , no other sources
	     */

	    MAP_DMA_setChannelControl(UDMA_PRI_SELECT | DMA_CH7_ADC14,
	        UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);

	    MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH7_ADC14,
	        UDMA_MODE_AUTO, (void*) &ADC14->MEM[11],
			image_buffer,
			1024);

	    /* Assigning/Enabling Interrupts */
	    MAP_DMA_assignInterrupt(DMA_INT1, 7);
	    MAP_Interrupt_enableInterrupt(INT_DMA_INT1);
	    MAP_DMA_assignChannel(DMA_CH7_ADC14);
	    MAP_DMA_clearInterruptFlag(7);
	    MAP_Interrupt_enableMaster();

	    /* Now that the DMA is primed and setup, enabling the channels. The ADC14
	     * hardware should take over and transfer/receive all bytes.The DMA is
	     * triggered after the conversion in single-channel conversion mode or
	     * after the completion of a sequence of channel conversions in
	     * sequence-ofchannels conversion mode. */
	    MAP_DMA_enableChannel(7);

	    /* Setting up the sample timer to automatically step through the sequence
	     * convert.
	     */
	    ADC14_enableSampleTimer(ADC_AUTOMATIC_ITERATION);

	    /* Triggering the start of the sample */
	    MAP_ADC14_enableConversion();
	    MAP_ADC14_toggleConversionTrigger();

The problem is now, that I can only initialize DMA transfers up to the size of 1024 items. How can I transfer more samples to the memory without waking up the CPU and without loosing any samples from the ADC?

Thanks a lot for your help!

  • I now figured out that my approach is wrong. The size of one DMA transfer needs to be 1 item, since the DMA needs to be triggered whenever a ADC sample is ready. So setting the transferSize to 1 does what it is supposed to do and a DMA transfer is started after the conversion of each ADC sample. However, the destination address is of course not increased, so all the samples are simply overwritten with each DMA transfer and at the end, only the last sample is available at buffer[0].

    Increasing the destination address and reinitialize the DMA transfer in the ISR after completion of a DMA transfer is not the solution, since the CPU should not be activated after each ADC sample. Using the different ADC Memories (ADC14MEM0...ADC14MEM31) does also not solve my problem.

    Is there a way to read a fixed number of ADC samples via DMA to the memory?
  • Hi Thomas,

    Due to hardware limitations, the DMA is able to transfer a maximum of 1024 samples in the DMA_setChannelTranfer function. A workaround is coding a DMA ISR occurring after 1024 transfers and re-initializing the DMA_setChannelTranfer’s destination address for the next 1024 bytes until all the samples are finished. An example ISR is posted below.

    /* Completion interrupt for eUSCIB1 TX */
    void dma_1_interrupt(void)
    {
    	DMACounter++;
    	/* If less than 1024 samples remaining, reconfigure DMA transfer size accordingly */
    	if(ADCSAMPLESIZE/1024 + 1 - DMACounter == 1)
    	{
    		MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH7_ADC12C,
    		                      UDMA_MODE_BASIC, (void*)  (ADC14_BASE +OFS_ADC14MEM0),
    		    				  (void*) (resultsBuffer+DMACounter*1024),
    		    				  ADCSAMPLESIZE%1024);
    		MAP_DMA_enableChannel(7);
    	}
    	/* Otherwise send the next 1024 samples */
    	else if(ADCSAMPLESIZE/1024-DMACounter >= 1)
    	{
    		MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_CH7_ADC12C,
    							  UDMA_MODE_BASIC, (void*)  (ADC14_BASE +OFS_ADC14MEM0),
    							  (void*) (resultsBuffer+DMACounter*1024),
    							  1024);
    		MAP_DMA_enableChannel(7);
    	}
    	else
    	{
    		/* Disabling the completion interrupt and disabling the DMA channels */
    		MAP_DMA_disableChannel(7);
    		MAP_DMA_disableInterrupt(INT_DMA_INT1);
    		PWM_Stop();
    		ADCTimer_Stop();
    		DataComplete=true;
    		DMACounter = 0;
    	}
    }

    Sincerely,

    Sean

  • OK, thanks a lot for your answer!

    But the DMA transfer then runs as fast as possible over 1024 read-outs of the ADCMEM register, if it is once triggered by the first ADC sample completion, right? Because I always get two or three times the same ADC value...
    I actually need to trigger the DMA transfer of one item after each acquisition of an ADC sample, such that the ADC can run continuously and I do not loose a sample or get a sample twice. Can this trigger only be done by setting the transferSize in DMA_setChannelTransfer to 1?
  • Hi Thomas,

    The ADC sample rate will need to be adjusted in your code accordingly depending on your application, but in regards to the DMA transferring, it will not miss any ADC samples if you store the samples in a buffer.

    Sincerely,
    Sean

  • Hi Sean,
    Thanks for your help! I used UDMA_MODE_AUTO by mistake, instead of UDMA_MODE_BASIC. That's why the DMA triggering did not work as wanted.
    Best,
    Thomas

  • Hey Thomas,

    Glad you got the DMA working properly on your end. No problem at all!

    Sincerely,
    Sean

**Attention** This is a public forum