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.

Using DMA to move ADC values into memory TMS28335

Other Parts Discussed in Thread: TMS320F28335

Hi,

Processor: TMS320F28335 150Mhz.

ADC -> DMA->Memory (Ping/Pong).

 I have been using the processor for some time, but recently found a bug in the operation of using the DMA to move data to memory from the ADC. I am using the ADC  sampling at a fast rate(faster than this example shows), with two simultaneous samples(4 A2D values) repeated 4 times before filling the ADC results registers. The DMA is set to transfer 4 values into memory. This is repeated 250 times per buffer, filling first one buffer and then the other buffer.

The Ping/Pong Buffers are DMA1,DMA2 (Uint16 DMA1[4][250];) This pare placed into two memory sections.

The problem I am having is that the DMA seems to be coping  the last 2 values from buffer DMA2 into buffer DMA1. and losing buffer DMA1’s last two values. In the example data(attached pdf). Note that at the end of DMA2 (553, 100) values are repeated at the start of DMA1(553,100). This same pattern can be seen over a large set of data, two values from the last part of buffer named DMA2 are replicated in DMA1.  Also at the end of DMA1 in the middle section, values seem to be missing. At the start of DMA2 the value 507 without any other values next to 507. Most sets of data shows 3 to 4 values before dropping to 0 for a few samples.

My DMA and ADC configuration code is as follows:

ADC:
AdcRegs.ADCTRL1.bit.CPS=1;
AdcRegs.ADCTRL1.bit.ACQ_PS =1;	// ADC_SHCLK;
AdcRegs.ADCTRL3.bit.ADCCLKPS =1; // ADC_CKPS;
// ADC will sample in Simultaneous sampling mode is selected.
AdcRegs.ADCTRL3.bit.SMODE_SEL = 0x1;
AdcRegs.ADCTRL1.bit.SEQ_OVRD = 0x1;		// No Wrap at MaxConv
AdcRegs.ADCTRL1.bit.SEQ_CASC = 0x1;        	// 1 Cascaded Mode
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 0x1;		// Int on MaxConv
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 0x1;  		// Set SEQ1 to CONV00

AdcRegs.ADCTRL1.bit.CONT_RUN = 0x1;			// Continuous Conv mode
AdcRegs.ADCMAXCONV.all = 1;   	// Set up ADC to perform 4 conversions for every SOC

AdcRegs.ADCCHSELSEQ1.bit.CONV00=0;	// data pair one
AdcRegs.ADCCHSELSEQ1.bit.CONV01=1;	// data pair two

AdcRegs.ADCCHSELSEQ1.bit.CONV02=0;	// data pair one
AdcRegs.ADCCHSELSEQ1.bit.CONV03=2;	// data pair three

AdcRegs.ADCCHSELSEQ2.bit.CONV04=0;	// data pair one
AdcRegs.ADCCHSELSEQ2.bit.CONV05=3;	// data pair four

AdcRegs.ADCCHSELSEQ2.bit.CONV06=0;	// data pair one
AdcRegs.ADCCHSELSEQ2.bit.CONV07=4;	// data pair five

DMA:
EALLOW;
DmaRegs.DMACTRL.bit.HARDRESET = 1; 	// Perform a hard reset on DMA
asm (" nop"); 				// one NOP required after HARDRESET
DmaRegs.DEBUGCTRL.bit.FREE = 1; 	// Allow DMA to run free on emulation suspend

// Set up SOURCE address:
DmaRegs.CH1.SRC_BEG_ADDR_SHADOW = (Uint32)&AdcMirror.ADCRESULT0;	// Point to beginning of source buffer
DmaRegs.CH1.SRC_ADDR_SHADOW =     (Uint32)&AdcMirror.ADCRESULT0;

// Set up DESTINATION address:
DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32)&DMABuf1[0][0];	    // Point to beginning of destination buffer
DmaRegs.CH1.DST_ADDR_SHADOW =     (Uint32)&DMABuf1[0][0];

   	// Set up BURST registers:
DmaRegs.CH1.BURST_SIZE.all = NumberOfChan-1;//Number of words(X-1)x-ferred in a burst
DmaRegs.CH1.SRC_BURST_STEP = 1;	 // Increment source addr between each word x-ferred
DmaRegs.CH1.DST_BURST_STEP = NumberOfSamples; // Increment dest addr between each word x-ferred

// Set up TRANSFER registers:
DmaRegs.CH1.TRANSFER_SIZE = NumberOfSamples-1;//(NumberOfSamples-1);                  // Number of bursts per transfer, DMA interrupt will occur after completed transfer
DmaRegs.CH1.SRC_TRANSFER_STEP = 1;// TRANSFER_STEP is ignored when WRAP occurs
DmaRegs.CH1.DST_TRANSFER_STEP = 0;// TRANSFER_STEP is ignored when WRAP occurs

// Set up WRAP registers: wrap on 4 reads of ADC
DmaRegs.CH1.SRC_WRAP_SIZE = NumberOfChan-1;				// Wrap source address after N bursts
DmaRegs.CH1.SRC_WRAP_STEP = 0;			    // Step for source wrap

DmaRegs.CH1.DST_WRAP_SIZE = 0;				// Wrap destination address after N bursts
DmaRegs.CH1.DST_WRAP_STEP = 1;				// Step for destination wrap

// Set up MODE Register:
DmaRegs.CH1.MODE.bit.PERINTSEL = DMA_SEQ1INT;	  // Passed DMA channel as peripheral interrupt source
DmaRegs.CH1.MODE.bit.PERINTE = PERINT_ENABLE;   // Peripheral interrupt enable
DmaRegs.CH1.MODE.bit.ONESHOT = ONESHOT_DISABLE; // Oneshot disable
DmaRegs.CH1.MODE.bit.CONTINUOUS = CONT_ENABLE;  // Continous enable
DmaRegs.CH1.MODE.bit.SYNCE = SYNC_ENABLE;      // Peripheral sync enable/disable
DmaRegs.CH1.MODE.bit.SYNCSEL = SYNC_SRC;      //Sync effects source or destination
DmaRegs.CH1.MODE.bit.OVRINTE = OVEFLOW_ENABLE;//Enable/disable the overflow interrupt
DmaRegs.CH1.MODE.bit.DATASIZE = SIXTEEN_BIT;      	// 16-bit/32-bit data size transfers
DmaRegs.CH1.MODE.bit.CHINTMODE = CHINT_END;		// Generate interrupt to CPU at end of transfer
DmaRegs.CH1.MODE.bit.CHINTE = CHINT_ENABLE;        	// Channel Interrupt to CPU enable

// Clear any spurious flags:
DmaRegs.CH1.CONTROL.bit.PERINTCLR = 1;  		// Clear any spurious interrupt flags
DmaRegs.CH1.CONTROL.bit.SYNCCLR = 1;    		// Clear any spurious sync flags
DmaRegs.CH1.CONTROL.bit.ERRCLR = 1; 	     	// Clear any spurious sync error flags

// Initialize PIE vector for CPU interrupt:
PieCtrlRegs.PIEIER7.bit.INTx1 = 1;              // Enable DMA CH1 interrupt in PIE
DmaRegs.CH1.CONTROL.bit.RUN = 1;

//--- Enable the DMA interrupt
IER |= 0x0040;				// Enable INT7 in IER to enable PIE group
EDIS;

I have been working in a DSP/BIOS project for most of the code development, but moved this example back into none RTOS based project to remove all outside code. The same basic problem of data not being filled into the correct memory location exists in both sets of code.

I am thinking that I have missed something in the data sheets or I am using the ADC/DMA in a way that just was not intended to be used.  As the DMA transfer works for filling most of the buffer with values that make sense, it it seems its a boundry case that when the buffer's are switching somthing is mixing up data. I have the following code that runs  just after the ISR posts a flag into the main while loop.  

 if(DMA_INTPost == 1)
	   {
		   DMA_INTPost=0; //clear  for next INT to set, we dont want to enter function unless we have new data.
		   iPingPong ^= 1;	// iPingPong toggles between 0 and 1
		   //--- Process the ADC data
		   if(iPingPong == 1)	// Ping buffer filling, process Pong bugger
		   {
			    ProcessBuffer=&DMABuf1[0][0];	    //process DMABuf1
			   	IERValue=IER;
			   	IER=0;
			    EALLOW;
			    asm (" nop");
			    asm (" nop");
				DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32)&DMABuf1[0][0];	    // load DMABuf1 as we are waiting for DMABuf2 to fill
			  	DmaRegs.CH1.DST_ADDR_SHADOW =     (Uint32)&DMABuf1[0][0];
			  	EDIS;
			  	IER=IERValue;
		   }
		   else				// Pong buffer filling, process Ping buffer
		   {
			    ProcessBuffer=&DMABuf2[0][0];// Disable EALLOW protected register access
			   	IERValue=IER;
			   	IER=0;
			    EALLOW;
			    asm (" nop");
			    asm (" nop");
			    DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32)&DMABuf2[0][0];	    // Point to beginning of destination buffer
			  	DmaRegs.CH1.DST_ADDR_SHADOW =     (Uint32)&DMABuf2[0][0];
			  	EDIS;
				IER=IERValue;
		   }

		   // handle dma data
		   for(i=0;i<250;i++)
			   MegaBuffer[0][MegaPtr++]=ProcessBuffer[i];

	   		if(MegaPtr>MegaBufSize)
	   			{
	   				 //Count+=1;
	   				MegaPtr=0;  // reset to zero
	   			}

Looking to see what input I can get. 

Thanks,

Caleb

e2epostdata.pdf
  • Any ideas? I am looking at other ways to trigger the DMA/ADC to work around the problem. If I can find any answers I will post back my results.

    Caleb

  • Caleb,

    I appologize for the delayed response.

    My team has taken a look at this issue and can not immediately see where the problem lies.  It will be next week but we will be looking at this in more detail.

    If you have had any further debug please go ahead and share any information you've found.

    Thanks,

    Jason

  • Hi Jason,

    Thanks for getting back to me on this.  I have been spending my time looking at some other options to solve the problem. 

    1. DSP/BIOS seems to be slower at the ISR response time.

    2. SYS/BIOS seems to be a possible solution, but is not working yet. I am using a ISR to restart the process of ADC-DMA. This way it would be single pass not ping pong with continuous run.

    3. Remove the RTOS. This works with some adjustments to the design requirements. However, much of the other parts of the system take a hit on coding.

    I have been spending the last week bring SYS/BIOS up with my existing project code base. I will open a new post with some questions I have run into with SYS/BIOS.

    However, the original question stands still... How to get the ADC and DMA to move data into memory without overhead of ISR's at each ADC completion. 

     Thanks,

    Caleb

  • Caleb,

    Can you send the HSPCLK value(in the SysCtrl Space) as well as the value of the static defines

     NumberofChan and NumberofSamples

    I just want to make sure I have the correct numbers in front of me as I work out the DMA/ADC on this one.

    Thanks!

    Best,
    Matthew
  • Hi Matt,

    Here are the items you requested....

    #define NumberOfChan 4
    #define NumberOfSamples 250 

    SysCtrlRegs.HISPCP.all = 0x0003;  150Mhz/6 = 25Mhz.

    SysCtrlRegs.LOSPCP.all = 0x0003; 

    Hope this helps.

    Caleb

  • Caleb,

    I want to make sure that I understand your intent for the code, and what I believe the code is doing from the snippet line up.

    Each DMA array has four 250 word sections, and there are 2 of those so we have a total of 2000 words of data.

    Per the setup of the ADC the channels are sampled in this order, with an ADC interrupt and corresponding DMA transfer happening every 4 conversions

    RESULT 0 = ADCINA0

    RESULT1 = ADCINB0

    RESULT2 = ADCINA1

    RESULT3 = ADCINB1

    ******ADC INT*******

    RESULT4 = ADCINA0

    RESULT5 = ADCINB0

    RESULT6 = ADCINA2

    RESULT7 = ADCINB2

    *****ADC INT *******

    RESULT8 = ADCINA0

    RESULT9 = ADCINB0

    RESULT10 = ADCINA3

    RESULT11 = ADCINB3

    *****ADC INT **********

    RESULT12 = ADCINA0

    RESULT13 = ADCINB0

    RESULT14 = ADCINA4

    RESULT15 = ADCINB4

    ******ADC INT **********

    This will get repeated over and over again until the ADC is stopped

    Now from the settings of the DMA here is what is happening in physical memory.  I want to stress that because I am not sure the standard decode of a 2 D array from C into SRAM although I assume it would be linear, although that would be something to check address wise.  All addresses assume a base of 0x0000, we can worry about where in SRAM this is located later.

    Memory Location                                                    ADCData

    0/1/2/3/.../249                                                           ADCINA0

    250/251/252/253/.../499                                        ADCINB0

    500/501/502/503/.../749                                        ADCINA1/ADCINA2/ADCINA3/ADCINA4/ADCINA1/ADCINA2/etc

    750/751/752/753/.../999                                        ADCINB1/ADCINB2/ADCINB3/ADCINB4/ADCINB1/ADCINB2/etc

    Assuming that the 2D array is mapped linearly to SRAM; is this how you want to sort the data? 

    Looking at your output file on the original post it is showing DMA1/DMA2 for every 250 sub array alternates, was this a post processing view to show the potential problem? 

    Could you give the base addresses of both DMABuf1 and DMABuf2?  I want to make sure there is no overlap physically, although it doesn't appear they are written at the same time.

    If you could please confirm the above, it will help to know if the DMA is doing what we expect or not.

    Best,

    Matthew

  • Hi Matthew,

    Thanks for your time. I am glad you turned my code into something you have mapped out in the above post.

    You have correctly interpreted the codes function.

    The addresses of DMABuf1, 2 are 0xC000 and 0xD000. These are placed on separate memory banks to keep the DMA and CPU from access conflicts.

    The output file is a processed set of data. The steps for this are shown as follows:
    extract DMABufx[0][y] data into memory:

    #define MegaBufSize 64000

    #pragma DATA_SECTION(MegaBuffer, "SramBufs"); //save to EXT. SRAM

    Uint16 MegaBuffer[1][MegaBufSize];

    #pragma DATA_SECTION(MegaBufferBuf, "SramBufs"); //save to EXT. SRAM

    Uint16 *MegaBufferBuf;

     

    if(iPingPong == 1)   // Ping buffer filling, process Pong bugger

       {

         ProcessBuffer=&DMABuf1[0][0];  // Point to beginning of destination buffer

       }

       else                           // Pong buffer filling, process Ping buffer

       {

         ProcessBuffer=&DMABuf2[0][0];// Disable EALLOW protected register access

       }

     

     

    for(i=0;i<250; i++)

                                MegaBuffer[0][MegaPtr++]=ProcessBuffer[i];

     

           if(MegaPtr>MegaBufSize)

             {

              MegaPtr=0;  // reset to zero

              }

    This set of data is then processed to be formatted as shown in the example data file with a python script that maps 10 lines of 25 memory location into a csv file(viewed in excel) for each DMABufx. This is repeated for 64000 A/D samples.

    I see the data being corrupted in memory before processing the data. So I am sure the data as seen in the processed file is the same as the memory of the processor.

  • Caleb,

    Appreciate the detail and help with my understanding.  From the description it sounds like from transfer to transfer(when we switch from DMA1-DMA2-DMA1-etc) that the source pointer is not re-loading from its shadow before the next transfer or the shadow register has changed and is reloaded incorrectly.

    Looking at your code, can we try to turn off the synchronization to see if an sync error is doing this?  Below is from your code, just change the enable to disable.  If this fixes the problem then I can try and understand if we really have had a sync error or it is false tripping.  From all your clock tree and sample info, the DMA should have no problem keeping up with the ADC, but there could be something else.
      Let me know the results, and I'll keep looking at that logic in parallel.

    DmaRegs.CH1.MODE.bit.SYNCE = SYNC_ENABLE;      // Peripheral sync enable/disable
    DmaRegs.CH1.MODE.bit.SYNCSEL = SYNC_SRC;      //Sync effects source or destination

  • I changed the synchronization setting and check the data. It is still creating currupted data. I am reviewing the data to see if I can present a better repersentation of how often the error happens. I am seeing it on both DMABuf1 and DMABuf2 edges  at random times during the 256 sets of dma transfers collected. 

  • Attached are graphs that show the data breaks when using both DMA sync enabled and disabled. The data as a whole looks about the same, while the same graphs might show more in the sync enabled mode.

    DMA data Graphs.pdf
  • Caleb,

    I'm trying to reproduce the environment on my local setup.  In the meantime would it be possible just to send the  raw memory contents from 0xC000 - 0xDFFF using the CCS "Save memory" and post it here?

    Thanks,

    Matthew

  • I have attached a memory dump.. it is in binary format. I renamed it to .txt for uploading. if you need it in .hex I can see if I can convert it.

  • Hi Matthew,

    Have you been able to create any example code to demonstrate the missing data from the DMA or the repeated data?

    I have adjusted the size of the buffer, from 250 to 2000 samples (DMABufX[4][2000]). Now I am seeing data that is getting dropped inside the dma transfer. It seems to be random in length of dropped data. If I use a set of pulses and check the rate of missed  tops in a set of 32 DMA transfers of 2000 data points, I get about 1.4% error rate. I can adjust the rate of pulses and see that the error rate stays about the same. I am using this as a systematic test rather than looking at each line of data. i was hoping to reduce the error rate by using a larger buffer. 

    If your team has any ideas for me to try, let me know. 

    Caleb

  • Caleb,
    Sorry for the delay, I'm going to get back onto this tomm.  I have some devices I was waiting for from our inventory and they got here on Friday of last week.  Will try and keep things more up to date on the thread.

    Best,

    Matthew

  • Thanks, any information you can provide will be great! Caleb

  • Hi Matt,

    Just checking to see if you have been able to do any testing. We are needing to move forward with our product, so any feedback you can provide will help. If your team finds that the ADC/DMA combo is not able to work with this configuration, would you have a recommendation for a alternate part we could test the idea with? One part I have seen is the new Dual Core 28377D. 

    Caleb

  • Hi Matt,

    Here is a snap shot of the DMA data in the buffers.

    Caleb

    DMAdata.docx
  • Thanks Matt for your time looking at this. In looking over the problem in detail, a solution was found that allows the DMA to fill buffers that are 256 words long, placed in exclusive memory. While this is not how the datasheets says the part should work, it is a workable solution, and will be used. There might be some other solutions that would allow  memory to be used  with both CPU and DMA, but this was not seeming to work for me. 

    Caleb