MUX to SAR to DMA two arrays

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 Using the CYC8KIT-050, I am trying to multiplex two analog inputs to a ADC_SAR and have the two channels' input samples go to separate arrays. Disclosure: I have had a case open on this for two weeks but the only response I got was a lame example of a single analog input pin sending 200 samples to Array1 and then 200 samples to Array2 using two TDs.  That's not what I need to do.  I need one sample from input A, then a sample from input B ... repeat until 2040 samples are collected in each array ( DMA_A[2040] and DMA_B[2040] ). Later I need to time align the samples in a FIR Filter (not shown in this simplified version). My problem is that I seem to only write to the first element of each array -- at best.

   

In the TopDesign I am using a LUT to select the MUX input. I'm using a countdown timer to generate an Interrupt when we have 4080 samples collected.  Each EOC ticks down the timer and generates a new SOC delayed by 1.5us through the PWM. I'm using two TDs, one for each array.

   

In the project attached, the DMA_Done ISR serves no purpose. In another version, I sent one sample to a single variable using one TD, then used the ISR to load it into either DMA_A or DMA_B. I checked to see if our sample count was even or odd to determine which array to load like this:

   

CY_ISR_PROTO(DMA_Done);
CY_ISR(DMA_Done)  // interrupt handler for DMA done
{
   if (adc_count & 1)   //  See if the array offset is even or odd
       DMA_B[adc_count] = adc_read;  // it's odd -- Load Right Channel
   else
       DMA_A[adc_count] = adc_read;  // it's even -- Load Left Channel

        adc_count++;  // adc_count is declared as volatile
        DMA_Done_flag = 1;
}

   

In the while (forever) loop, I print the values of DMA_A elements 1,2,and 3 on the top row of the LCD. I  print DMA_B elements 1,2,3 on the sencond row. NOTE: To test this you must assure that at least one channel's input connection to the potentiometer on P3[6] in the cydwr. Note that the dummy initalizations in lines 169,170 do not get overwritten by data as shown on the LCD.

   

If this method is not feasible, perhaps someone could suggest a C++ structure that could separate every other element from a single array without using CPU cycles in a for loop. The single starting array would be:
sampleA[1],sampleB[1],sampleA[2],sampleB[2],sampleA[3],sampleB[3]...

   

But my FIR Filter will have to see:
sampleA[1],sampleA[2],sampleA[3]... in channel A, and
sampleB[1],sampleB[2],sampleB[3]... in channel B

   

Workspace Bundle attached. Thank you.

0 Likes
1 Solution
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

Looking just at the code you posted: for each array, you only write to every second element (0, 2, 4 for DMA_A, and 1, 3, 5 for DMA_B).

   

Using the DMA; what you need is called 'scatter-gather DMA' (and is explained under that name in the TRM). What you do is:

   
        
  • create two TDs for your DMA, each pointing to the corresponding array
  •     
  • link them in a loop, so they are executed after each other
  •     
  • transfer count is 2040 for each, and set the
  •     
  • 'request per burst' is set to 1, so only single bytes are transferred each time
  •     
  • maybe you want to set 'preserve TDs' so the DMA can start over after filling the buffer
  •     
  • you need to set 'increment destination address' on both TDs
  •    
   

There is an example doing nearly what you need, but without the memory (it just sends data from the three channels to three DACs): http://www.cypress.com/?id=4&rID=60629

View solution in original post

0 Likes
16 Replies
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

Looking just at the code you posted: for each array, you only write to every second element (0, 2, 4 for DMA_A, and 1, 3, 5 for DMA_B).

   

Using the DMA; what you need is called 'scatter-gather DMA' (and is explained under that name in the TRM). What you do is:

   
        
  • create two TDs for your DMA, each pointing to the corresponding array
  •     
  • link them in a loop, so they are executed after each other
  •     
  • transfer count is 2040 for each, and set the
  •     
  • 'request per burst' is set to 1, so only single bytes are transferred each time
  •     
  • maybe you want to set 'preserve TDs' so the DMA can start over after filling the buffer
  •     
  • you need to set 'increment destination address' on both TDs
  •    
   

There is an example doing nearly what you need, but without the memory (it just sends data from the three channels to three DACs): http://www.cypress.com/?id=4&rID=60629

0 Likes
JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 Additional info: While this didn't solve my problem, I just noticed an error source in my code in my previous post. Instead of saying: if(adc_count & 1) ... I should have said: if(1 && adc_count)...  The former would do an assignment while the latter does a compare and catches such a possible error.  Still working on the problem, though.

0 Likes
JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 Thanks, hli, My previous post was about my own comment, not yours.

   

That example might prove to be helpful for hardware parameters. I'm looking at it now. Actually, Request per Burst = 1 means True, not the number of bytes and it is set to 1 in my code.  My samples are 12 bits (2 bytes).  I have tried several permutations of 

   


CyDmaTdSetConfiguration(DMA_1_TD[0], 2, DMA_1_TD[1], TD_INC_DST_ADR | TD_AUTO_EXEC_NEXT);
CyDmaTdSetConfiguration(DMA_1_TD[1], 2, DMA_1_TD[0], DMA_1__TD_TERMOUT_EN | TD_INC_DST_ADR |   TD_AUTO_EXEC_NEXT);
CyDmaTdSetAddress(DMA_1_TD[0], LO16((uint32)ADC_SAR_SAR_WRK0_PTR), LO16((uint32)DMA_A));
CyDmaTdSetAddress(DMA_1_TD[1], LO16((uint32)ADC_SAR_SAR_WRK0_PTR), LO16((uint32)DMA_B));

   


How does one specify "Preserve TDs?"
I'll keep experimenting. Meanwhile any further tips are highly welcome as I still don't have it working.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

Just to be sure you have seen these ? -

   

 

   

    

   

         

   

http://www.cypress.com/?rID=37793     AN52705     Getting Started with DMA

   

http://www.cypress.com/?rID=82680     AN84810     PSoC® 3 and PSoC 5LP Advanced DMA Topics

   

http://www.cypress.com/?rID=44335     AN61102 PSoC® 3 and PSoC 5LP - ADC Data Buffering Using DMA

   

http://video.cypress.com/video-library/search/dma/     Videos on DMA

   

https://www.youtube.com/results?search_query=dma+psoc Videos on DMA (some overlap)

   

 

   

The dual array approach certainly you should be able to get working, but why not one

   

array, then odd address would be one channel, even the other. Would that also fix your

   

time aligned problem in filtering ? Additionally use a D or T clocked by EOC to drive the mux

   

channel selection as a simplification ? As far as delay for mux settling, your conversion rate

   

pretty low for SAR, why not double conversion rate and throw every other sample away ? Just

   

a thought.

   

 

   

Regards, Dana.

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

If you want to make the code totally clear, you should actually use

   

if (1==(1 & adcCount))

   

. '&' does bit-manipulation (so it gets you the lowest bit of the value), while '&&' does logical conditions (meaning it treats both the 1 and adcValue as boolean values and the AND-connects them - which is not what you want.

HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

I know that 'request per burst==1' means true, and thats what I intended.

   

If you want to transfer 2 bytes per burst, ou need to specify that ('bytes per burst'). Look at AN61102 for an example how to do that with a single channel. Maybe its better to start with a single channel first in your case and get that working. That way you know that at least that part works.

   

Btw: is there a reason not two use two SAR ADCs? That way you could easily parallelize the DMAs. (Actually, thats even doable with a single ADC: set up two DMAs with the proper configuration, and ping-pong the DMA request between them)

0 Likes
JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 @hli, Those are good ideas. Your if logic suggestion is well taken. It looks like I will need to go to two DMAs. I am not sure how to ping pong each new sample selecting either this dma or that dma. I'm studying on that.  One 2-byte sample from MUX input 1 going to DMA1 and the next 2-byte sample from input 2 going to DMA2 and repeat.
 

   

@danaknight, I had seen most of those ANs but the reading the second one again gave me the idea of connecting the Timer's comp output to the DMA trq to terminate the session. I'm not sure how to configure hardware to select every other sample of an array. Perhaps that's related to my comment in the previous paragraph.  Is this done with TDs? Do I create two Channels on a DMA or one channel on two DMAs?

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

This shows an interesting approach using an intermediate register,

   

not sure if it would serve your case but interesting.

   

 

   

    

   

          

   

https://www.youtube.com/watch?v=_WReMrxEBCs     20 bit ADC DMA

   

   

 

   

Regards, Dana.

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

To ping-pong between the two MUX inputs and the two DMAs, just use a LUT to enable the state logic.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

I have not tried this but seems like it should work pretty straight forward -

   

Note I did not connect trq's to anything, you could use them to end DMA

   

activity so you would connect to Tc out on the counter, both trq's.

   

 

   

0 Likes
JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 Thanks to you guys! I feel like I'm sitting at the feet of Socrates. You make the right comments to lead me to my own exploration.

   

Danaaknight, I love that diagram.  The logic gates are perfectly suited to my purpose -- and I haven't even tried it yet. Why didn't I think of that?  This could eliminate my Sequencing Logic.  The only reason I put in the PWM is that one of the Example Projects suggested that it is necessary to let the MUX settle.  But let's remove it to simplify.  If so, I'll add it back in.  Fantastic -- simple logic gates.  I'll let you know after I try it. Stay tuned.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

The reason this works is the eos output allows us to switch the mux

   

to the next channel while the SAR is converting, it is cool for that

   

reason. I was not aware of this control until I looked at SAR config.

   

Although one shortcoming is no AC specs for this output in datasheet.

   

But I think if you use a scope and look at eos and eoc, difference will

   

be settling time new channel experiences, if its << uS range all

   

should be good. 20 pF and 1 K R-C spurs will settle in 8.3 time

   

constants in a 12 bit system. Thats ~ 160 nS. See -

   

 

   

    

   

          http://www.analog.com/static/imported-files/application_notes/AN-1024.pdf

   

 

   

Socrates no, we all learn from the customer base quite often.

   

 

   

Regards, Dana.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

I had to make 2 changes, mux mode to mux which then required the clock,

   

and counter from fixed to UDB because fixed clock input limitations. This

   

compiles w/o error. There is a warning which I do not think is an issue

   

that effects operation.

   

 

   

I also connected the trq's to force a DMA terminate, although I think thats

   

redundant because TD will have termination parameters. You can try with/

   

without.

   

 

   

Regards, Dana.

   

 

   

0 Likes
JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 It seems to be working. Thanks to all who helped.

0 Likes
ETRO_SSN583
Level 9
Level 9
250 likes received 100 sign-ins 5 likes given

I got help from tech support on the clock warning issues,

   

below fixes that problem.

   

 

   

Regards, Dana.

   

 

   

JeCo_264681
Level 5
Level 5
100 replies posted 50 replies posted 25 replies posted

 Yes, I meant to say that I figured that out.  The counter data sheet indicates that it is important to sync the Count input with the clock. so you also sync the qt to avoid asynchronous signals messages.

0 Likes