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 DMA

Other Parts Discussed in Thread: HALCOGEN

Hi,

I've been working with ADC and DMA on a TMS570. ADC1 Group1 consists of 6 channels. ADC1 Memory is 16/16/32 for Group Even, Group1 and Group2, hence the results for the 6 channales are at address 0xFF3E0040 to 0xFF3E0054. A simple DMA transfer from these addresses to a RAM buffer works fine. What I've been wondering is, if it is possible to have a RAM buffer 10 times the size of the channel number and store 10x6 channel samples in that buffer before getting a DMA interrupt (for exampel for averaging purposes). I've attached a picture to clarify the goal.  I've been playing with all kind of setups of DMA/ADC  (element, frame, block ...) but never really got the desired result. Is that even possible?


Regards,

Juergen

 


 

  • Hello Juergen,

    Reviewing the TRM, it seems that the ADC is capable of generating a DMA REQ for each conversion or for each block of conversions (6 in you case). What I am not certain of is if the "BLOCK" as referenced in the TRM is the same as the "BLOCK" terminology in the DMA. Based on the FRAME, ELEMENT and BLOCK diagrams in the DMA section for a Frame transfer, it seems that the ADC BLOCK could be treated as FRAME by the DMA. If this is the case you could set up the DMA control packet to transfer 10 frames of 6 elements where each ADC block would issue the DMA REQ signal for each DMA Frame of 6 elements. When all 10 frames have been transfered, the DMA interrupt would fire. As I stated, I am not 100% certain about this interpretation of the TRM material, but it something you could try.

    In the mean time, I will forward this post to one of our ADC experts to get their inputs as well in case he would have any additional advice or corrections to what I have stated above.
  • Hi Juergen,

    As Chuck mentioned you can configure the ADC to generate a DMA request for every 6 conversions for group1. This would form one frame for the DMA. You can configure the DMA control packet to transfer one block of 10 frames with each frame being 6 elements. Also the trigger type would be for a frame transfer.

    Let me know how it goes.

    Regards,

    Sunil

  • Forgot to mention : you need to configure the DMA to generate an interrupt upon completion of the block transfer. This will them match the requirement you stated.
  • Thanks Chuk and Sunil  for your quick reply, it actually helped a lot. I've tryed something very similar but kept missing an important setting. I post the solultion in case someone else reads this post with a similar problem. My main problem was the source pointer being incremented all the time. Setting the address mode to ADDR_OFFSET and the element-offset to 4 did the job. It increments the source pointer 6 times (equals my size of elements/frame) and resets it to the inital address after for the following frame. I assume autoinit has to be enabled to do so. Here is my control package:

        adc1_dmaCTRLPKT.SADD        = 0xFF3E0040;           //source address - FIFO of ADC1 group1
        adc1_dmaCTRLPKT.DADD        = (CPU_INT32U)adcDmaDest;   //destination address
        adc1_dmaCTRLPKT.CHCTRL    = 0;                           //channel to be triggered after this one, in this case no other channel will be triggered
        adc1_dmaCTRLPKT.FRCNT      = 10;     //frame count
        adc1_dmaCTRLPKT.ELCNT       = 6;     //element count - 6 adc values to be read 
        adc1_dmaCTRLPKT.ELDOFFSET = 0;     //element destination offset
        adc1_dmaCTRLPKT.ELSOFFSET = 4;     //element source offset
        adc1_dmaCTRLPKT.FRDOFFSET = 0;     //frame destination offset
        adc1_dmaCTRLPKT.FRSOFFSET = 0;     //frame source offset
        adc1_dmaCTRLPKT.PORTASGN  = 4;     //PORTB
        adc1_dmaCTRLPKT.RDSIZE    = ACCESS_32_BIT;    //read size
        adc1_dmaCTRLPKT.WRSIZE    = ACCESS_32_BIT;    //write size
        adc1_dmaCTRLPKT.TTYPE     = FRAME_TRANSFER;    //transfer type - FRAME_TRANSFER/BLOCK_TRANSFER
        adc1_dmaCTRLPKT.ADDMODERD = ADDR_OFFSET;    //address mode read
        adc1_dmaCTRLPKT.ADDMODEWR = ADDR_INC1;    //address mode write: ADDR_FIXED/ADDR_INC1/ADDR_OFFSET
        adc1_dmaCTRLPKT.AUTOINIT  = AUTOINIT_ON;    //autoinit

    The DMA setup the ADC1_GRP1:

        adcREG1->G1DMACR =  (uint32) (0x06 << 16) | /* G1 BLOCKS: requires G1 BLK XFER to be enabled                                        */
                                                  (uint32) (0x00 << 03) | /* DMA G1 END: DMA after ADC has completed the conversions for all             */
                                                                                      /* channels selected in group1.                                                                             */
                                                  (uint32) (0x01 << 02) | /* G1 BLK XFER: ADC module generates a DMA request when the ADC has    */
                                                                                      /* written G1 BLOCKS number of buffers into the Group1 memory.                     */
                                                                                      /* If G1 BLK XFER bit is set to '1', G1 DMA EN bit is ignored and DMA               */
                                                                                      /* requests will be generated every time the Threshold Counter                           */
                                                                                      /* reaches '0' from a count value of 1.                                                                   */
                                                 (uint32) (0x01 << 01);  /* G1 DMA EN: ADC module generates a DMA transfer when the ADC               */
                                                                                      /* has written to the Group1 memory. The G1 BLK XFER bit must                       */
                                                                                      /* cleared to ‘0’ for this DMA request to be generated                                           */

  • Hi Juergen,

    How is your ADC triggered? Is the ADC group1 configured for a single sequence of 6 channels each time it is triggered? In that case, it is better to configure the DMA request to be generated when the conversion sequence for those 6 channels is completed. Then you can actually use the FIFO port for the group1 results and have the DMA read out the 6 results in order from the same fixed address (0xFFF7C0B0). So now you don't have to increment the source address at all.
  • Hi Sunil,

    now that u mention the FIFO, I read about it in the TRM but forgot about it as I worked with the ADC. Right now I trigger the ADC by software, later I'll most likely use HET. Everytime the ADC is triggered I read 6 channels. To be honest, I didn't know there are other ways of doing this, at least I can't see how I could set up something more fancy in HalCoGen. Using the FIFO would have made my life a lot easier :) Just to make sure I fully understand it: I read from fixed source address (ADG1BUFFER: 0xFFF7C0B0) and the FIFO does all the work for me? Right now I am working on a double buffering scheme. I'll double my destination buffer and the number of frames I acutally neeed and set up the DMA to throw a Half/Full-Block interrupt. In an alternating manner I'll work with the upper/lower half of the buffer while the DMA fills the other half with new data. Hope that makes sense :)
  • Hi Juergen,

    Yes, reading from the FIFO allows you to keep the source address fixed. One another suggestion that you can implement is to employ a second group, say group2, with the same 6 channels selected as group1. You can then trigger them alternately (one with the rising edge, one with the falling edge of the same HET 50% PWM signal), and enable separate DMA channels to transfer the conversion results from these two groups (same setup, just different source and destination addresses). This way the DMA could be transferring the conversion results for one group while the conversions are ongoing in the other group. You could significantly increase the sampling rate this way, as now you don't have to wait for the DMA transfers to complete before triggering the group again.

    Regards,
    Sunil
  • Hi Sunil,

    that's actually a good idea. However I am struggling a litlte with HW-triggered ADC conversion. I can'get get the RTI_COMP0 trigger to trigger conversions at all. It's used by the OS, so I am sure it's running. The HET-trigger seems to work. BUt still facing problems there. It seems like I still have to call adcStartConversion(). I expected to call it once and then HET triggers converisons on rising/falling edge. I guess that is not the case and wonder where I should call this function. Makes sense to me to call it once at the beginngen and then in "conversion done"- interrupt, but that sounds like quite a overhead to me. I tried continuous conversion, but the seem literally continous, not triggered by HET anymore.
  • Hi Juergen,

    I have attached an example code project that shows how to configure the ADC, the N2HET and the DMA for maximizing the sampling rate for three channels. This is implemented on the RM46x MCU so that it also uses the enhanced channel selection mode supported by the ADC on those MCUs.

    I am not sure which precise part number you are using. However, the N2HET and the DMA setup can still be identical to those in the example.

    Hope it helps.6523.RM46x_ADC_DMA.zip

    Regards,

    Sunil

  • Hi Sunil,

    thanks for the example, I am working with a TMS570LS3137ZWT. I took a quick peek into your example. I'll go into detail on monday. What did suprise me a little on first glance:
    adcREG1->ADEVCHNSELMODECTRL = 0xA; // enable enhanced channel selection mode for event group
    adcREG1->ADEVMAXCOUNT = 24; // 24 conversions total
    adcREG1->ADEVCURRCOUNT = 0; // clear event group current_count counter
    .
    .
    adcLUT1->eventGroup[i].EV_EXT_CHN_MUX_SEL = 0;
    .
    .


    TMS570 doesn't have these registers/LUT.

    I am having trouble understanding "continous conversion". Does the mean the ADC is running at maximum cylce time independtly of the hw trigger? Seems like the code above code would help with that but I am not sure how I can get the TMS570 to the desired behavior.
  • Hi Juergen,

    The LS3137 does not support the enhanced channel selection mode, so these registers and the LUT are not available. You can still modify the example to just use the regular channel selection mode and select from up to 24 channels available on the LS3137.

    The continuous conversion mode is designed to convert continuously once it is triggered. This makes it less usable for applications where the ADC timing is to be controlled by a known timing event such as a PWM signal or an RTI event. This mode is typically used if you want to have one or more channels continuously monitored using the ADC's magnitude compare interrupt feature. In this case, you don't really care to read out every conversion result and only need an interrupt to be generated when the selected input goes above or below a certain threshold.

    Your use case is best suited for triggering via an RTI compare interrupt or a HET PWM, with the data from one sequence being transferred by the DMA and then processed by the CPU.

    Regards,
    Sunil
  • Hi,

    just wanted to share my experiences in case someone is trying to accomplish something similar. I've set up the ADC to be triggered by HET14, transferring conversion results into a double buffer located in RAM. Everything is actually quite straight forward, however it seemed like something was going wrong during debugging. The ADC conversions were stalled after stopping the debugger. Without stopping the debugger or without the debugger attached at all, everything was fine, samples were continously collected. I figured out that halting the debugger didn't stop the HET. I assume that's a problem for the ADC/DMA. I solved it by setting the IS bit in t NHET's global configuration register which stops the NHET on suspend.

    Regards,
    Juergen
  • Hi Juergen,

    Both the N2HET and the ADC have a mode which allows these modules to continue operating even when the CPU execution is stopped.

    This is done via the "Ignore Suspend" bit in the HETGCR register for the N2HET, and via the "Continue On Suspend" bit of the ADOPMODECR register for the ADC. By setting these two bits, you can choose to trigger and convert analog inputs even with the CPU halted.

    Regards, Sunil

  • Hi Sunil,

    ...... which was my default setting and led to the stalling problem. I supposed there is a problem with DMA if ADC continuous to sample when CPU is halted.

    Regards,
    Juergen
  • Hi Juergen,

    Actually even the DMA module has a mode in which it ignores the fact that the CPU is halted. This is done by configuring the 'DEBUG_MODE" field of the DMA GCTRL register to 0x00, telling the DMA module to "Ignore Suspend". In this way, your analog signal acquisition can happen without any CPU intervention.

    Regards,
    Sunil
  • Hello good day, I have the RM46 Launchpad, I have tried to run this code sample that you shared:

    5327.RM46x_ADC_DMA.zip

     when I debug the program, the following errors appear me:

    I'm new with DMA, I wonder if there is any way to fix this!

    Regards

    Martin Valencia

  • Hi Martin,

    It makes sense because of these lines 77 & 78 in the sys_main.c file:

    uint32 adc1_EV_results[43200];
    uint32 adc1_G1_results[43200];

    43200*4 = 172,800 bytes.

    And the RM46 only has 192K Bytes of RAM.

    The only chip we have that could run this program with those size arrays in internal memory would be RM57 but Sunil may have written to run on an RM46 HDK with SDRAM as well. The LaunchPad doesn't have SDRAM expansion memory... and doesn't have the package w. EMIF either.

    If you want to try this program you could try changing the size of the array results to something smaller.
    But then I think you also need to change the 3600 in the DMA control packet.
    (3600 * 24) = 86400. The DMA is set to do 16-bit transfers so this means 86400 * 2 = 172800.

    In other words if you were to cut the array sizes by 4

    uint32 adc1_EV_results[10800];
    uint32 adc1_G1_results[10800];

    Then the 3600 in the DMA control packet should also scale to 3600/4 = 900.
  • 2273.DMA_RM46.zip

    Hi Anthony, thank you very much for the tips!
    I've made the changes but I did not get the answer I want, it seems that the problem lies elsewhere!

    I made the change of values as you indicated me and you can see in the image below that also changed the values, but more weight variables have not changed at all!

    to be more specific this is the mistake that sends me:

    I have attached my project at the top if you want to check it out!

    Best Regards

    Martin