Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 29

Thread: Reconfigure ADC via a DMA transfer to allow multiple Channel Acquisition

  1. #1

    Reconfigure ADC via a DMA transfer to allow multiple Channel Acquisition

    I am trying to sample 4 to 8 Analog input signals coming from microphones on the Teensy 3.1 simultaneously.
    Unfortunately there are only two ADC's available on the Teensy 3.1. So to measure 4-8 signals I want to sequentially measure all the target pins and then start again with the first pin. To achieve high sample rates I use DMA channels to lighten the load of the CPU.

    The first DMA channel is configured to copy the measured samples from the ADC's output register to a buffer. I tested a sketch in which I only sampled one signal and It seemed to work.
    To measure multiple channels I added a second DMA channel. The second DMA channel is configured to rewrite the ADC's configuration register with different values each time the first DMA channel is activated. The target pin numbers are kept in static array and the second DMA channel is supposed to copy the pin number to the ADC's register and thereby switching to a different target pin and starting a new configuration.

    Unfortunately this does not seem to work. Any idea what I am doing wrong here?

    Thanks in advance

    Code:
    #include <string.h>
    #include "ADC.h" 
    #include <DMAChannel.h>
    
    #define BUF_SIZE 256
    #define NO_BUFFERS 4
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer[NO_BUFFERS][BUF_SIZE];
    
    volatile int ibuf;
    volatile int obuf;
    
    ADC *adc = new ADC();
    
    DMAChannel* dma1 = new DMAChannel(false);
    DMAChannel* dma2 = new DMAChannel(false);
    
    const uint32_t ChannelsCfg [] =  { 6, 7, 15, 4 };  //pin 20-23
    
    void setup() {
    
      Serial.begin(9600); // USB is always 12 Mbit/sec
      delay(1000);
      Serial.println("Ready");
      delay(1000);
      
    }
    
    void loop() {
      
      // clear buffer
      for (int i = 0; i < NO_BUFFERS; ++i){
        for (int j = 0; j < BUF_SIZE; ++j){
          adcbuffer[i][j] = 50000;
        }
      }
    
      ibuf = 0;
      obuf = 0;
    
      setup_dma();
      setup_adc();
      
      for (int i = 0; i < BUF_SIZE; ++i){
        while(obuf==ibuf);
        Serial.write((uint8_t *)adcbuffer[obuf],512);
        obuf=(obuf+1)&3;
      }
      
      for (;;) {}
    
    } 
    
    void setup_dma() {
    
      dma1->begin(true);              // allocate the DMA channel first
      dma1->TCD->SADDR = &ADC0_RA;    // where to read from
      dma1->TCD->SOFF = 0;            // source increment each transfer
      dma1->TCD->ATTR = DMA_TCD_ATTR_SSIZE(1);
      dma1->TCD->NBYTES_MLNO = 2;     // bytes per transfer
      dma1->TCD->SLAST = 0;
      dma1->destinationBuffer(adcbuffer[0], 512);   // destinaton sizeof(adcbuffer[0])
      dma1->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma1->disableOnCompletion();    // require restart in code
      dma1->interruptAtCompletion();
      dma1->attachInterrupt(dma1_isr);
      dma1->enable();
    
      dma2->begin(true);              // allocate the DMA channel first
      dma2->sourceCircular(ChannelsCfg, 4);
      dma2->destination(ADC0_SC1A);  
      //dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0); 
      //read somewhere that DMAMUX_SOURCE_ADC0 is only able to trigger one DMA channel
      dma2->triggerAtTransfersOf(*dma1);
      dma2->enable();
      
    } 
    
    void setup_adc() {
    
      adc->setAveraging(32); // set number of averages
      adc->setResolution(16); // set bits of resolution
      adc->setReference(INTERNAL);
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      ADC0_SC1A = ChannelsCfg[0]; //write target to register to start first conversion
    
    } 
    
    
    void dma1_isr(void) {
    
        ibuf=(ibuf+1)&3;
        dma1->destinationBuffer(adcbuffer[ibuf],512); //sizeof(adcbuffer[ibuf])
        dma1->enable();
        dma1->clearInterrupt();
    
    }

  2. #2
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,321
    Did you have a look onto Freescale's application note AN 4590? There, the multi-channel acquisition by using DMA reconfiguration of one single ADC is described in every detail.

  3. #3
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Have you by chance looked at the ADC Library on the forums?

  4. #4
    Quote Originally Posted by Donziboy2 View Post
    Have you by chance looked at the ADC Library on the forums?
    Yes, I am using the ADC library. I include ADC.h on line 2.
    I instantiated an ADC object on line 13 and I use the ADC object in the function setup_adc() on line 78-84 to set different properties of the ADC0.

    I can't use analogRead() because it is a blocking method, meaning I can't use the processor in the meantime. It also does a calibration each time it is called making it slow.

    If I had a single analog pin to measure I would use startContinuous() from the ADC library. I tested this and it worked fine but since I have to measure multiple signals at a time I need to switch between the pins after each ADC conversion.
    Last edited by SaileNav; 09-28-2015 at 11:49 AM. Reason: minor markup

  5. #5
    Quote Originally Posted by Theremingenieur View Post
    Did you have a look onto Freescale's application note AN 4590? There, the multi-channel acquisition by using DMA reconfiguration of one single ADC is described in every detail.
    I based my sketch on a document on the freescale website: Measuring all ADC inputs on FRDM-KL25 using DMA.

    I am looking at your suggestion, Using DMA to Emulate ADC Flexible Scan Mode on Kinetis K Series, and it is very similar to what I am doing, because I am using triggerAtTransfersOf function of the DMA channel library.

    In DMAChannel.h line number 441-449:
    Code:
    // Use another DMA channel as the trigger, causing this
    // channel to trigger after each transfer is makes, except
    // the its last transfer.  This effectively makes the 2
    // channels run in parallel until the last transfer
    void triggerAtTransfersOf(DMABaseClass &ch) {
    	ch.TCD->BITER = (ch.TCD->BITER & ~DMA_TCD_BITER_ELINKYES_LINKCH_MASK)
    	  | DMA_TCD_BITER_ELINKYES_LINKCH(channel) | DMA_TCD_BITER_ELINKYES_ELINK;
    	ch.TCD->CITER = ch.TCD->BITER ;
    }
    Probably the devil will be in the details. Let me do a detailed comparison and get back to you.

  6. #6
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Quote Originally Posted by SaileNav View Post
    Yes, I am using the ADC library. I include ADC.h on line 2.
    I instantiated an ADC object on line 13 and I use the ADC object in the function setup_adc() on line 78-84 to set different properties of the ADC0.

    I can't use analogRead() because it is a blocking method, meaning I can't use the processor in the meantime. It also does a calibration each time it is called making it slow.

    If I had a single analog pin to measure I would use startContinuous() from the ADC library. I tested this and it worked fine but since I have to measure multiple signals at a time I need to switch between the pins after each ADC conversion.
    How fast do you need to read the ADC's and how often?

    p.s.
    The reason I pointed out the ADC Library is I was pretty sure DMA was already supported.
    Last edited by Donziboy2; 09-28-2015 at 05:12 PM.

  7. #7
    Quote Originally Posted by Donziboy2 View Post
    How fast do you need to read the ADC's and how often?
    I would like to sample 4 to 8 microphones simultaneously for 5 secs. Each microphone signal should be sampled at at least 12 KHz.
    After the 5 secs there is a possibility to wait until data transfers to my laptop are complete. I want to do these 5 sec. measurements a few 100 times.

  8. #8
    Code:
    #include <string.h>
    #include "DMAChannel.h"
    #include "ADC.h" 
    
    #define BUF_SIZE 256
    #define NO_BUFFERS 4
    
    
    ADC *adc = new ADC(); // adc object
    
    DMAChannel* dma0 = new DMAChannel(false);
    DMAChannel* dma1 = new DMAChannel(false);
    
    const uint8_t ChannelsCfg [] =  { 6, 7, 15, 4 };
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer[NO_BUFFERS][BUF_SIZE];
    volatile int ibuf;
    volatile int obuf;
    
    void setup() {
      char password[] = "start";
      uint8_t index = 0;
      char c = '0';
      
      Serial.begin(9600); // USB is always 12 Mbit/sec
      delay(100);
      // data acquisition will start with a synchronisation step:
      while(index!=5){
        while(Serial.available() == 0){
          delay(100);// polls whether anything is ready on the read buffer - nothing happens until there's something there
        }
        c = Serial.read();
        if(password[index]==c){
          index++;
        }else{
          index = 0;
        }
        delay(1);
      }
      Serial.println("Ready");
      delay(2000);
      
    
    }
    
    void loop() {
      
      // clear buffer
      for (int i = 0; i < NO_BUFFERS; ++i){
        for (int j = 0; j < BUF_SIZE; ++j){
          adcbuffer[i][j] = 50000;
        }
      }
     
      Serial.println("setup");
      delay(2000); 
      ibuf = 0;
      obuf = 0;
    
       
      setup_adc(23);
      setup_dma(); 
      
      Serial.println("dma0 Channel");
      print_config(dma0);
    
      Serial.println("dma1 Channel");
      print_config(dma1);
      
      for (int i = 0; i < BUF_SIZE; ++i){
        while(obuf==ibuf);
        //Serial.write((uint8_t *)adcbuffer[obuf],512); 
        Serial.println("read buffer and send data");
        obuf=(obuf+1)&3;
      }
      
      for (;;) {}
    
    }
    
    void setup_dma() {
    
      dma0->begin(true);              // allocate the DMA channel first
      dma0->TCD->SADDR = &ADC0_RA;    // where to read from
      dma0->TCD->SOFF = 0;            // source increment each transfer
      dma0->TCD->ATTR = 0x101;
      dma0->TCD->NBYTES = 2;     // bytes per transfer
      dma0->TCD->SLAST = 0;
      dma0->TCD->DADDR = &adcbuffer[0][0];// where to read from
      dma0->TCD->DOFF = 2; 
      dma0->TCD->BITER = 256;
      dma0->TCD->CITER = 256;    
      dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma0->disableOnCompletion();    // require restart in code
      dma0->interruptAtCompletion();
      dma0->attachInterrupt(dma0_isr);
      
      dma1->begin(true);              // allocate the DMA channel first
      dma1->TCD->SADDR = &ChannelsCfg[0];
      dma1->TCD->SOFF = 1;            // source increment each transfer
      dma1->TCD->ATTR = 0;
      dma1->TCD->SLAST = -4;
      dma1->TCD->BITER = 4;
      dma1->TCD->CITER = 4;
      dma1->TCD->DADDR = &ADC0_SC1A;
      dma1->TCD->NBYTES = 1;
      dma1->TCD->DOFF = 0;
      dma1->TCD->CITER = 0x01;
      dma1->TCD->BITER = 0x01;
      dma1->triggerAtTransfersOf(*dma0);
      dma1->triggerAtCompletionOf(*dma0);
      dma1->interruptAtCompletion();
      dma1->attachInterrupt(dma1_isr);
    
      dma0->enable();
      dma1->enable();
      
    } 
    
    void print_config(DMAChannel* dma) {
        Serial.print("channel\t");
        Serial.println(dma->channel);
        Serial.print("SADDR\t");
        Serial.println((uint32_t)dma->TCD->SADDR);
        Serial.print("SOFF\t");
        Serial.println((uint32_t)dma->TCD->SOFF);
        Serial.print("ATTR\t");
        Serial.println((uint32_t)dma->TCD->ATTR);
        Serial.print("NBYTES\t");
        Serial.println((uint32_t)dma->TCD->NBYTES);
        Serial.print("SLAST\t");
        Serial.println((uint32_t)dma->TCD->SLAST);
        Serial.print("DADDR\t");
        Serial.println((uint32_t)dma->TCD->DADDR);
        Serial.print("DOFF\t");
        Serial.println((uint32_t)dma->TCD->DOFF);
        Serial.print("CITER\t");
        Serial.println((uint32_t)dma->TCD->CITER);
        Serial.print("DLASTSGA\t");
        Serial.println((uint32_t)dma->TCD->DLASTSGA);
        Serial.print("CSR\t");
        Serial.println((uint32_t)dma->TCD->CSR);
        Serial.print("BITER\t");
        Serial.println((uint32_t)dma->TCD->BITER);
        Serial.println();
    }
    
    void setup_adc(int pin) {
      adc->setAveraging(32); // set number of averages
      adc->setResolution(16); // set bits of resolution
      adc->setReference(INTERNAL);
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      adc->adc0->startContinuous(pin);//  ADC0_SC3 |= ADC_SC3_ADCO;  ADC0_SC1A = get_pin(pin);   // set to hardware input channel
      
      
    } 
    
    
    void dma0_isr(void) {
        Serial.println("call dma0 isr");
        ibuf=(ibuf+1)&3;
        dma0->destinationBuffer(adcbuffer[ibuf],512); //sizeof(adcbuffer[ibuf])
        dma0->enable();
        dma0->clearInterrupt();
    }
    
    void dma1_isr(void) {
        dma1->clearInterrupt();
        Serial.println("call dma1 isr");
    }
    
    
    // convert pin name to hardware pin
    // teensy 3.1 only
    
    #if defined(__MK20DX256__)
    
    int 
    get_pin(int pin)
    {
     
    static const uint8_t channel2sc1a[] = {
      5, 14, 8, 9, 13, 12, 6, 7, 15, 4,
      0, 19, 3, 19+128, 26, 18+128, 23,
      5+192, 5+128, 4+128, 6+128, 7+128, 4+192
    // A15  26   E1   ADC1_SE5a  5+64
    // A16  27   C9   ADC1_SE5b  5
    // A17  28   C8   ADC1_SE4b  4
    // A18  29   C10  ADC1_SE6b  6
    // A19  30   C11  ADC1_SE7b  7
    // A20  31   E0   ADC1_SE4a  4+64
    };
    
    int index;
    
      if (pin <= 13) {
        index = pin;      // 0-13 refer to A0-A13
      } else if (pin <= 23) {
        index = pin - 14; // 14-23 are A0-A9
      } else if (pin >= 26 && pin <= 31) {
        index = pin - 9;  // 26-31 are A15-A20
      } else if (pin >= 34 && pin <= 40) {
        index = pin - 24; // 34-37 are A10-A13, 38 is temp sensor,
                    // 39 is vref, 40 is A14
      } else {
        return 5;
      }
    
    return channel2sc1a[index];
    
    }   // get_pin()
    
    #endif
    I made some changes since my last post. I replaced some higher level functions of the DMAChannel library with low level register assignations in setup_dma() function. I looked at the manual and the example provided by Theremingenieur and set the register to the best of my knowledge, but no success yet.

    To make it easier to debug I added a print statement to the interrupt service routines (ISR), called dma0_isr and dma1_isr.
    I now get the following output on my laptop via minicom

    Code:
    Ready
    setup
    
    dma0 Channel
    channel 0
    SADDR   1073983504
    SOFF    0
    ATTR    257
    NBYTES  2
    SLAST   0
    DADDR   536838656
    DOFF    2
    CITER   33536
    DLASTSGA        0
    CSR     298
    BITER   33536
    
    dma1 Channel
    channel 1
    SADDR   16392
    SOFF    1
    ATTR    0
    NBYTES  1
    SLAST   4294967292
    DADDR    1073983488
    DOFF    0
    CITER   1
    DLASTSGA        0
    CSR     130
    BITER   1
    
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    call dma1 isr
    The print statement of the dma1's ISR is repeated 47 times and then minicom does not receive any output anymore. So i think the sketch crashes. The print statement of the dma0's ISR is never printed.

    On a positive note, the fact that the ISR of dma1 is called means that the channel linking works.
    ADC0--(end-of-conversion)-->dma0--(transfer,completion)-->dma1

    Anybody any idea what I am doing wrong here?
    Thanks in advance.
    Last edited by SaileNav; 10-01-2015 at 01:38 PM. Reason: making statements more clear

  9. #9
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Hello SaileNav, did you ever have luck getting it to work?

  10. #10
    Quote Originally Posted by Donziboy2 View Post
    Hello SaileNav, did you ever have luck getting it to work?
    Yes, I got it working, but I don't know what the exact problem was anymore.
    Do you need a similar functionality?

  11. #11
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Yes, I have two projects i'm working on that would benefit greatly from being able to offload the ADC's from the CPU. It would also allow me to increase sample rates without affecting the rest of the project.

    Im actually amazed its not a more commonly used feature of the Teensy.

  12. #12
    Hi Donziboy2,

    I was also very frustrated that nobody ever had done it and shared it before.
    So in this file on my github repo, you can find a working version.

    Hope that helps you,
    Sailenav

  13. #13
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    That looks interesting, it will take me a while to get the gist of it, C is not my best language lol. Much better with hardware vs software.
    Below is questions after browsing.

    Not to familiar with string.h is that for serial comms?
    I'm going to assume the D(n) ISR's are for external inputs. Not sure what the purpose of more then 1 is..

    This looks to be the ADC channels? I take it the values are from the datasheet. edit. never mind I see your using both ADC's, still trying to figure out how those values translate to actual pins, AD4 and AD5 on ADC1 have 2 pins each(ADC1_SE4a and ADC1_SE4b, same for 5, both sets are brought out on the T3.1/3.2), so how is the pin selected.... If im reading this poorly written 1400 page document right....
    Code:
    const uint16_t ChannelsCfg_0 [] =  { 0x46, 0x47, 0x4F, 0x44 };  //ADC0: ad6, ad7, ad15, ad4
    const uint16_t ChannelsCfg_1 [] =  { 0x44, 0x45, 0x46, 0x47 };  //ADC1: ad4, ad5, ad6, ad7

    This looks to be moving the internal buffer to an external one for use, but I have no clue how that syntax works lol.
    Code:
    obuf_0=(ibuf_0+2) % NO_BUFFERS;
    Thought you were using 16 bit values?
    Code:
    Serial.write((uint8_t *)adcbuffer_0[obuf_0],2*BUF_SIZE);
    Are you calibrating the ADC's? From what I understand they need to be calibrated at least once after startup.
    Last edited by Donziboy2; 02-16-2017 at 12:10 AM.

  14. #14
    Quote Originally Posted by Donziboy2 View Post
    Thought you were using 16 bit values?
    Code:
    Serial.write((uint8_t *)adcbuffer_0[obuf_0],2*BUF_SIZE);
    This question is an easy one.
    We just send it in chunks of 8bit. I think it is required if you use Serial.write, I am not sure.
    At the receiver(PC)'s side the 16 bit values are reconstructed.

    Quote Originally Posted by Donziboy2 View Post
    I'm going to assume the D(n) ISR's are for external inputs. Not sure what the purpose of more then 1 is..
    Yes ISR's are for external inputs. We needed multiple external input signals to tag the data.
    The inputs are both triggers and taggers. We recorded data for different gestures, and we wanted to now for each record that is sent to the computer, which gesture invoked it.


    Quote Originally Posted by Donziboy2 View Post
    This looks to be the ADC channels? I take it the values are from the datasheet. edit. never mind I see your using both ADC's, still trying to figure out how those values translate to actual pins, AD4 and AD5 on ADC1 have 2 pins each(ADC1_SE4a and ADC1_SE4b, same for 5, both sets are brought out on the T3.1/3.2), so how is the pin selected.... If im reading this poorly written 1400 page document right....
    Code:
    const uint16_t ChannelsCfg_0 [] =  { 0x46, 0x47, 0x4F, 0x44 };  //ADC0: ad6, ad7, ad15, ad4
    const uint16_t ChannelsCfg_1 [] =  { 0x44, 0x45, 0x46, 0x47 };  //ADC1: ad4, ad5, ad6, ad7
    Yeah the documentation really sucks. At the time we figured it out, but now I can't recall the details, but you can easily check it which pins I choose. Just use my values and apply a high signal to those inputs.


    Quote Originally Posted by Donziboy2 View Post
    This looks to be moving the internal buffer to an external one for use, but I have no clue how that syntax works lol.
    Code:
    obuf_0=(ibuf_0+2) % NO_BUFFERS;
    It is a circular buffer that is constantly being overwritten by the converted values of the ADC. Once one of the digital inputs of pin 2,4,6,8 is triggered, then a certain number of values is sent to the computer. We wanted to start with what was already in the buffer, so that we have a bit of what happened before the trigger, that is why we start at 2 positions after the ibuf pointer. You have to do % NO_BUFFERS, becuase it is circular.

    Quote Originally Posted by Donziboy2 View Post
    Are you calibrating the ADC's? From what I understand they need to be calibrated at least once after startup.
    I don't know exactly, maybe it happens in adc->adc0->enableDMA()

    Hope that helps you further,
    Sailenav

  15. #15
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Every little bit helps, I should have a chance this weekend to drag out some gear and start playing with it on some hardware. Im going to contact Pedvide at some point once I understand this a little more and see if he wants to try adding any of this to the ADC Library. He already has a DMA ring buffer example that uses just 1 pin per ADC, so it may be possible to add this in without to much fuss.
    Last edited by Donziboy2; 02-17-2017 at 10:30 AM.

  16. #16
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Well, it sort of works.

    Copy/paste is only pulling a small portion of it.
    Code:
    Ready
    Start
    1
    WO=SkXM`mdUfwaLEd@:\msWLKT2X]RNC?S>KSJE-:Tng=A:HQ^Bh> 4GwGEk;iTSJQ?H3FDGc_�LK�\{MG/V+M�HZ*PiIOTVGJPLX*lh[R=
    c[\Tb�nWl~f$c&WZQej[C4m
    v_YUeZMH:S8SX8hikqTdaichZchxiVlcYAK)C.}1[LFW!GPQ#\Z>S@UiV^]IVJEQWZiqQyslVa*]�^4oxTU|%ZPg\dYJ`ygj_b6ke9[aLA^UawZmJ`fR>kT:K_k'\NP9WM`cNEDllgHGJDMVL=B9ARQW	KNLZ8F7QtXN=R\T_^XN�Zjg&]wwmo%VgQMFOC<F8:?PTbEbU>tBLg@\|SxN<ZIAG\OSVG1[evF[zrXZv��LZs[Loy,[vbBZ�nhZor{k[$anj\)[sg[c)sd[e/ [zZu}AZ`g5.Ze"ys[\auZkZwq[;rwU[.gss9[gzTZxe_Zf[t8Zlh$[bfZh!}O$[kd'*Z_j`=ZgsS"[wZz8[s	z[CqEw^F[BixYZlp1Z} Zxg,Zl}GZ>nMoK	[qWmSZp1pBZ"ymw

  17. #17
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Got it working, had to change Serial.write to Serial.print, for some reason it was just sending garbage with serial.write.
    I also noticed one strange result from the data. The last ADC pin from both lists(ChannelsCfg) is actually sent first. So instead of 0->1->2->3 you get 3->0->1->2.
    I had to add a delay timer to the transmits also since using a wire to jumper the ISR pin was causing several transmits at a time. I only did it to D2 ISR since I was mainly interested in the output data and not what pin.
    I will play around with the settings and see what I can find, here is my modifications to your code SaileNav.


    Code:
    #include <string.h>
    #include "DMAChannel.h"
    #include "ADC.h" 
    
    #define BUF_SIZE 256
    #define NO_BUFFERS 30
    //#define BITS_NO_BUF 5
    
    ADC *adc = new ADC(); // adc object
    
    DMAChannel* dma0 = new DMAChannel(false);
    DMAChannel* dma1 = new DMAChannel(false);
    DMAChannel* dma2 = new DMAChannel(false);
    DMAChannel* dma3 = new DMAChannel(false);
    
    
    const uint16_t ChannelsCfg_0 [] =  { 0x46, 0x47, 0x4F, 0x44 };  //ADC0: ad6(A6), ad7(A7), ad15(A8), ad4(A9)
    const uint16_t ChannelsCfg_1 [] =  { 0x44, 0x45, 0x46, 0x47 };  //ADC1: ad4(A17), ad5(A16), ad6(A18), ad7(A19)
    //45
    const int ledPin = 13;
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_0[NO_BUFFERS][BUF_SIZE];
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_1[NO_BUFFERS][BUF_SIZE];
    
    volatile int ibuf_0;
    volatile int ibuf_1;
    
    volatile int obuf_0;
    volatile int obuf_1;
    
    volatile int d2_active;
    volatile int d4_active;
    volatile int d6_active;
    volatile int d8_active;
    
    void setup() {
      // initialize the digital pin as an output.
      pinMode(ledPin, OUTPUT);
      delay(500); 
      
      ibuf_0 = 0;
      ibuf_1 = 0;
      obuf_0 = 0;
      obuf_1 = 0;
    
      d2_active = 0;
      d4_active = 0;
      d6_active = 0;
      d8_active = 0;
      
      pinMode(2, INPUT_PULLUP);
      pinMode(4, INPUT_PULLUP);
      pinMode(6, INPUT_PULLUP);
      pinMode(8, INPUT_PULLUP);
    
      attachInterrupt(2, d2_isr, FALLING);
      attachInterrupt(4, d4_isr, FALLING);
      attachInterrupt(6, d6_isr, FALLING);
      attachInterrupt(8, d8_isr, FALLING);
    
      // clear buffer
      for (int i = 0; i < NO_BUFFERS; ++i){
        for (int j = 0; j < BUF_SIZE; ++j){
          adcbuffer_0[i][j] = 50000;
          adcbuffer_1[i][j] = 50000;
        }
      }
       
      setup_adc();
      setup_dma(); 
      
    
    }
    elapsedMillis debounce;
    
    
    void loop() {
    
      char password[] = "start";
      uint8_t index = 0;
      char c = '0';
      
      Serial.begin(9600);
      delay(100);
      // data acquisition will start with a synchronisation step:
      while(index!=5){
        while(Serial.available() == 0){
          delay(100);// polls whether anything is ready on the read buffer - nothing happens until there's something there
        }
        c = Serial.read();
        if(password[index]==c){
          index++;
        }else{
          index = 0;
        }
        delay(1);
      }
      Serial.println("Ready");
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(500);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(500); 
      
      Serial.println("Start");
    
      while(1){
        uint8_t pin = 0;
        if(d2_active) pin=1;
        if(d4_active) pin=2;
        if(d6_active) pin=3;
        if(d8_active) pin=4;
    
        if(pin>0){
    
          Serial.println(pin);
          
          obuf_0=(ibuf_0+2) % NO_BUFFERS;
          obuf_1=(ibuf_1+2) % NO_BUFFERS;
          for (int i = 0; i < 100; ++i){
            int a = adcbuffer_0[obuf_0][i];
            Serial.print(a);
            Serial.print(" , ");
            int b = adcbuffer_1[obuf_1][i];
            Serial.println(b);
           // Serial.write((uint8_t *)adcbuffer_0[obuf_0],2*BUF_SIZE); 
           // Serial.write((uint8_t *)adcbuffer_1[obuf_1],2*BUF_SIZE); 
            //Serial.println("read buffer and send data");
            obuf_0=(obuf_0+1) % NO_BUFFERS;
            obuf_1=(obuf_1+1) % NO_BUFFERS;
    
            while(obuf_0==ibuf_0);
            while(obuf_1==ibuf_1);
          }
    
          digitalWrite(ledPin, HIGH);   // set the LED on
          delay(50);                  // wait for a second
          digitalWrite(ledPin, LOW);    // set the LED off
    
          d2_active = 0;
          d4_active = 0;
          d6_active = 0;
          d8_active = 0;
          
        }
      }
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(100);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(100);   
    }
    
    void setup_dma() {
      dma0->begin(true);              // allocate the DMA channel 
      dma0->TCD->SADDR = &ADC0_RA;    // where to read from
      dma0->TCD->SOFF = 0;            // source increment each transfer
      dma0->TCD->ATTR = 0x101;
      dma0->TCD->NBYTES = 2;     // bytes per transfer
      dma0->TCD->SLAST = 0;
      dma0->TCD->DADDR = &adcbuffer_0[0][0];// where to write to
      dma0->TCD->DOFF = 2; 
      dma0->TCD->DLASTSGA = -2*BUF_SIZE;
      dma0->TCD->BITER = BUF_SIZE;
      dma0->TCD->CITER = BUF_SIZE;    
      dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma0->disableOnCompletion();    // require restart in code
      dma0->interruptAtCompletion();
      dma0->attachInterrupt(dma0_isr);
      
      dma1->begin(true);              // allocate the DMA channel 
      dma1->TCD->SADDR = &ChannelsCfg_0[0];
      dma1->TCD->SOFF = 2;            // source increment each transfer
      dma1->TCD->ATTR = 0x101;
      dma1->TCD->SLAST = -8;
      dma1->TCD->BITER = 4;
      dma1->TCD->CITER = 4;
      dma1->TCD->DADDR = &ADC0_SC1A;
      dma1->TCD->DLASTSGA = 0;
      dma1->TCD->NBYTES = 2;
      dma1->TCD->DOFF = 0;
      dma1->triggerAtTransfersOf(*dma0);
      dma1->triggerAtCompletionOf(*dma0);
    
      dma2->begin(true);              // allocate the DMA channel 
      dma2->TCD->SADDR = &ADC1_RA;    // where to read from
      dma2->TCD->SOFF = 0;            // source increment each transfer
      dma2->TCD->ATTR = 0x101;
      dma2->TCD->NBYTES = 2;     // bytes per transfer
      dma2->TCD->SLAST = 0;
      dma2->TCD->DADDR = &adcbuffer_1[0][0];// where to write to
      dma2->TCD->DOFF = 2; 
      dma2->TCD->DLASTSGA = -2*BUF_SIZE;
      dma2->TCD->BITER = BUF_SIZE;
      dma2->TCD->CITER = BUF_SIZE;    
      dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1);
      dma2->disableOnCompletion();    // require restart in code
      dma2->interruptAtCompletion();
      dma2->attachInterrupt(dma2_isr);
      
      dma3->begin(true);              // allocate the DMA channel 
      dma3->TCD->SADDR = &ChannelsCfg_1[0];
      dma3->TCD->SOFF = 2;            // source increment each transfer
      dma3->TCD->ATTR = 0x101;
      dma3->TCD->SLAST = -8;
      dma3->TCD->BITER = 4;
      dma3->TCD->CITER = 4;
      dma3->TCD->DADDR = &ADC1_SC1A;
      dma3->TCD->DLASTSGA = 0;
      dma3->TCD->NBYTES = 2;
      dma3->TCD->DOFF = 0;
      dma3->triggerAtTransfersOf(*dma2);
      dma3->triggerAtCompletionOf(*dma2);
    
      dma0->enable();
      dma1->enable();
      
      dma2->enable();
      dma3->enable();
      
    } 
    
    void setup_adc() {
      adc->adc0->setResolution(16); // set bits of resolution
      //adc->adc0->setReference(ADC_REF_1V2);
      adc->adc1->setResolution(16); // set bits of resolution
      //adc->adc1->setReference(ADC_REF_1V2);`
      
      ADC1_CFG2 |= ADC_CFG2_MUXSEL;
      
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      adc->adc1->enableDMA();
      
      ADC0_SC1A = ChannelsCfg_0[3];
      ADC1_SC1A = ChannelsCfg_1[3];
    } 
    
    void d2_isr(void) {
      if(debounce > 1000){
        d2_active = 1;
        debounce = 0;
        }
        else{return;}
    }
    
    void d4_isr(void) {
        d4_active = 1;
    }
    void d6_isr(void) {
        d6_active = 1;
    }
    
    void d8_isr(void) {
        d8_active = 1;
    }
    
    void dma0_isr(void) {
        ibuf_0=(ibuf_0+1) % NO_BUFFERS;
        dma0->TCD->DADDR = &adcbuffer_0[ibuf_0][0];
        dma0->clearInterrupt();
        dma0->enable();
    }
    
    void dma2_isr(void) {
        ibuf_1=(ibuf_1+1) % NO_BUFFERS;
        dma2->TCD->DADDR = &adcbuffer_1[ibuf_1][0];
        dma2->clearInterrupt();
        dma2->enable();
    }
    Last edited by Donziboy2; 03-07-2017 at 11:13 AM.

  18. #18
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Had some time to play around with this, still not sure why the adc values are shifted out of order by one. But here is a breakdown table of the available pins.
    I did not dig into the Bandgap or Vrefout pins yet. I did look at the A/B channel for ADC1 and found that an ADCx_CFG2[MUXSEL] setting change is required to access the A channels.

    ADC Channel Breakdowns.pdf
    I will update the sheet as I get time to verify the unconfirmed pins.
    Last edited by Donziboy2; 03-07-2017 at 11:12 AM.

  19. #19
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Had time to do some poking around with a scope, found some interesting things and possible bugs.
    I have attached a breakdown so far of the times needed to complete a run from the DMA controlled ADC's.
    I performed the tests with a buffer size of 256 and 128, all values are number of filled buffers per second. I drive a pin high and then low when the DMA ISR fires.

    A few interesting things to note.
    10, 12, and 13 bit readings have the same exact time for most of the tests I ran.
    Conversion Low Speed at 10bit seems to be broken and not inline with the other settings at the same conversion speed.
    The number of averages seems to default to > 1 if not set in the code.
    Averages of 2 and 4 took the same amount of time.

    Teensy 3.1, Arduino 1.6.12, Teensyloader 1.32, I did not look at the ADC data just how long it was taking so I cant say if the data was better or worse per setting.
    Default settings means that only the Bit resolution was set, everything else was commented out.
    Click image for larger version. 

Name:	ADC Channel Breakdowns pg2.jpg 
Views:	166 
Size:	103.0 KB 
ID:	9929

    Attached my test code based on SaileNav's example. I have tried to simplify it to make it easier for others, hope I succeeded.

    Code:
    // Converted to only 1 buffer per ADC, reduced to a small example.
    //Based on Example by SaileNav
    
    
    #include "DMAChannel.h"
    #include "ADC.h" 
    
    #define BUF_SIZE 256
    
    ADC *adc = new ADC(); // adc object
    
    DMAChannel* dma0 = new DMAChannel(false);
    DMAChannel* dma1 = new DMAChannel(false);
    DMAChannel* dma2 = new DMAChannel(false);
    DMAChannel* dma3 = new DMAChannel(false);
    
    
    const uint16_t ChannelsCfg_0 [] =  { 0x46, 0x47, 0x4F, 0x44 };  //ADC0: ad6(A6), ad7(A7), ad15(A8), ad4(A9)
    const uint16_t ChannelsCfg_1 [] =  { 0x44, 0x45, 0x46, 0x47 };  //ADC1: ad4(A17), ad5(A16), ad6(A18), ad7(A19)
    
    const int ledPin = 13;
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_0[BUF_SIZE];
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_1[BUF_SIZE];
    
    volatile int d2_active;
    
    void setup() {
      // initialize the digital pin as an output.
      pinMode(ledPin, OUTPUT);
      delay(500); 
    
      d2_active = 0;
      
      pinMode(2, INPUT_PULLUP);
      pinMode(4, OUTPUT);
      pinMode(6, OUTPUT);
    
      attachInterrupt(2, d2_isr, FALLING);
    
      // clear buffer
      for (int i = 0; i < BUF_SIZE; ++i){
          adcbuffer_0[i] = 50000;
          adcbuffer_1[i] = 50000;
        
      }
       
      setup_adc();
      setup_dma(); 
      
    }
    elapsedMillis debounce;
    
    
    void loop() {
    
      char password[] = "start";
      uint8_t index = 0;
      char c = '0';
      
      Serial.begin(9600);
      delay(100);
      // data acquisition will start with a synchronisation step:
      while(index!=5){
        while(Serial.available() == 0){
          delay(100);// polls whether anything is ready on the read buffer - nothing happens until there's something there
        }
        c = Serial.read();
        if(password[index]==c){
          index++;
        }else{
          index = 0;
        }
        delay(1);
      }
      Serial.println("Ready");
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(500);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(500); 
      
      Serial.println("Start");
    
      while(1){
        uint8_t pin = 0;
        if(d2_active) pin=1;
    
        if(pin>0){
          
          for (int i = 0; i < BUF_SIZE; i = i + 4){
            int a = adcbuffer_0[i];
            Serial.print(a);
            Serial.print(".");
            int b = adcbuffer_0[i+1];
            Serial.print(b);
            Serial.print(".");
            int c = adcbuffer_0[i+2];
            Serial.print(c);
            Serial.print(".");
            int d = adcbuffer_0[i+3];
            Serial.print(d);
            Serial.print("...");                
            int e = adcbuffer_1[i];
            Serial.print(e);        
            Serial.print(".");
            int f = adcbuffer_1[i+1];
            Serial.print(f);        
            Serial.print(".");
            int g = adcbuffer_1[i+2];
            Serial.print(g);        
            Serial.print(".");                        
            int h = adcbuffer_1[i+3];
            Serial.println(h);
    
          }
    
          digitalWrite(ledPin, HIGH);   // set the LED on
          delay(50);                  // wait for a second
          digitalWrite(ledPin, LOW);    // set the LED off
    
          d2_active = 0;     
        }
      }
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(100);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(100);   
    }
    
    void setup_dma() {
      dma0->begin(true);              // allocate the DMA channel 
      dma0->TCD->SADDR = &ADC0_RA;    // where to read from
      dma0->TCD->SOFF = 0;            // source increment each transfer
      dma0->TCD->ATTR = 0x101;
      dma0->TCD->NBYTES = 2;     // bytes per transfer
      dma0->TCD->SLAST = 0;
      dma0->TCD->DADDR = &adcbuffer_0[0];// where to write to
      dma0->TCD->DOFF = 2; 
      dma0->TCD->DLASTSGA = -2*BUF_SIZE;
      dma0->TCD->BITER = BUF_SIZE;
      dma0->TCD->CITER = BUF_SIZE;    
      dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma0->disableOnCompletion();    // require restart in code
      dma0->interruptAtCompletion();
      dma0->attachInterrupt(dma0_isr);
      
      dma1->begin(true);              // allocate the DMA channel 
      dma1->TCD->SADDR = &ChannelsCfg_0[0];
      dma1->TCD->SOFF = 2;            // source increment each transfer
      dma1->TCD->ATTR = 0x101;
      dma1->TCD->SLAST = -8;          // num ADC0 samples * 2
      dma1->TCD->BITER = 4;
      dma1->TCD->CITER = 4;
      dma1->TCD->DADDR = &ADC0_SC1A;
      dma1->TCD->DLASTSGA = 0;
      dma1->TCD->NBYTES = 2;
      dma1->TCD->DOFF = 0;
      dma1->triggerAtTransfersOf(*dma0);
      dma1->triggerAtCompletionOf(*dma0);
    
      dma2->begin(true);              // allocate the DMA channel 
      dma2->TCD->SADDR = &ADC1_RA;    // where to read from
      dma2->TCD->SOFF = 0;            // source increment each transfer
      dma2->TCD->ATTR = 0x101;
      dma2->TCD->NBYTES = 2;     // bytes per transfer
      dma2->TCD->SLAST = 0;
      dma2->TCD->DADDR = &adcbuffer_1[0];// where to write to
      dma2->TCD->DOFF = 2; 
      dma2->TCD->DLASTSGA = -2*BUF_SIZE;
      dma2->TCD->BITER = BUF_SIZE;
      dma2->TCD->CITER = BUF_SIZE;    
      dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1);
      dma2->disableOnCompletion();    // require restart in code
      dma2->interruptAtCompletion();
      dma2->attachInterrupt(dma2_isr);
      
      dma3->begin(true);              // allocate the DMA channel 
      dma3->TCD->SADDR = &ChannelsCfg_1[0];
      dma3->TCD->SOFF = 2;            // source increment each transfer
      dma3->TCD->ATTR = 0x101;
      dma3->TCD->SLAST = -8;          // num ADC1 samples * 2
      dma3->TCD->BITER = 4;
      dma3->TCD->CITER = 4;
      dma3->TCD->DADDR = &ADC1_SC1A;
      dma3->TCD->DLASTSGA = 0;
      dma3->TCD->NBYTES = 2;
      dma3->TCD->DOFF = 0;
      dma3->triggerAtTransfersOf(*dma2);
      dma3->triggerAtCompletionOf(*dma2);
    
      dma0->enable();
      dma1->enable();
      
      dma2->enable();
      dma3->enable();
      
    } 
    
    void setup_adc() {
      //ADC0
      //adc->setAveraging(16); // set number of averages
      adc->adc0->setResolution(16); // set bits of resolution
      //adc->setConversionSpeed(ADC_VERY_LOW_SPEED); // change the conversion speed
      //adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
      //adc->adc0->setReference(ADC_REF_1V2);
      
      //ADC1
      //adc->setAveraging(16, ADC_1); // set number of averages
      adc->adc1->setResolution(16); // set bits of resolution
      //adc->setConversionSpeed(ADC_VERY_LOW_SPEED, ADC_1); // change the conversion speed
      //adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
      //adc->adc1->setReference(ADC_REF_1V2);`
      
      ADC1_CFG2 |= ADC_CFG2_MUXSEL;
      
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      adc->adc1->enableDMA();
      
      ADC0_SC1A = ChannelsCfg_0[3];
      ADC1_SC1A = ChannelsCfg_1[3];
    } 
    
    void d2_isr(void) {
      if(debounce > 200){
        d2_active = 1;
        debounce = 0;
        }
        else{return;}
    }
    
    void dma0_isr(void) {
        dma0->TCD->DADDR = &adcbuffer_0[0];
        dma0->clearInterrupt();
        dma0->enable();
        digitalWriteFast(4, HIGH);
        digitalWriteFast(4, LOW);
    }
    
    void dma2_isr(void) {
        dma2->TCD->DADDR = &adcbuffer_1[0];
        dma2->clearInterrupt();
        dma2->enable();
        digitalWriteFast(6, HIGH);
        digitalWriteFast(6, LOW);
    }
    Last edited by Donziboy2; 03-09-2017 at 12:29 AM.

  20. #20
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Still trying to figure out why the values are out of order. Unless I make progress I may just live with the bug and code around it :/

    Here is an 8 channel ADC0/4 channel ADC1 example. I did not change the serial print for the extra channels so first 4 are first line and then second 4 on second line etc.


    Code:
    // Converted to only 1 buffer per ADC, reduced to a small example.
    //Based on Example by SaileNav
    
    
    #include "DMAChannel.h"
    #include "ADC.h" 
    
    #define BUF_SIZE 256
    
    ADC *adc = new ADC(); // adc object
    
    DMAChannel* dma0 = new DMAChannel(false);
    DMAChannel* dma1 = new DMAChannel(false);
    DMAChannel* dma2 = new DMAChannel(false);
    DMAChannel* dma3 = new DMAChannel(false);
    
    
    const uint16_t ChannelsCfg_0 [] =  { 0x46, 0x47, 0x4F, 0x44, 0x4C, 0x4D, 0x4E, 0x45 };  //ADC0: ad6(A6), ad7(A7), ad15(A8), ad4(A9), ad12(A5), ad13(A4), ad14(A1), ad5b(A0)
    const uint16_t ChannelsCfg_1 [] =  { 0x44, 0x45, 0x46, 0x47 };  //ADC1: ad4(A17), ad5(A16), ad6(A18), ad7(A19)
    
    const int ledPin = 13;
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_0[BUF_SIZE];
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_1[BUF_SIZE];
    
    volatile int d2_active;
    
    void setup() {
      // initialize the digital pin as an output.
      pinMode(ledPin, OUTPUT);
      delay(500); 
    
      d2_active = 0;
      
      pinMode(2, INPUT_PULLUP);
      pinMode(4, OUTPUT);
      pinMode(6, OUTPUT);
    
      attachInterrupt(2, d2_isr, FALLING);
      
      Serial.begin(9600);
      
      // clear buffer
      for (int i = 0; i < BUF_SIZE; ++i){
          adcbuffer_0[i] = 50000;
          adcbuffer_1[i] = 50000;
        
      }
       
      setup_adc();
      setup_dma(); 
      
    }
    elapsedMillis debounce;
    
    
    void loop() {
    
      while(1){
        uint8_t pin = 0;
        if(d2_active) pin=1;
    
        if(pin>0){
          
          for (int i = 0; i < BUF_SIZE; i = i + 4){
            int a = adcbuffer_0[i];
            Serial.print(a);
            Serial.print(".");
            int b = adcbuffer_0[i+1];
            Serial.print(b);
            Serial.print(".");
            int c = adcbuffer_0[i+2];
            Serial.print(c);
            Serial.print(".");
            int d = adcbuffer_0[i+3];
            Serial.print(d);
            Serial.print("...");                
            int e = adcbuffer_1[i];
            Serial.print(e);        
            Serial.print(".");
            int f = adcbuffer_1[i+1];
            Serial.print(f);        
            Serial.print(".");
            int g = adcbuffer_1[i+2];
            Serial.print(g);        
            Serial.print(".");                        
            int h = adcbuffer_1[i+3];
            Serial.println(h);
    
          }
    
          digitalWrite(ledPin, HIGH);   // set the LED on
          delay(50);                  // wait for a second
          digitalWrite(ledPin, LOW);    // set the LED off
    
          d2_active = 0;     
        }
      }
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(100);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(100);   
    }
    
    void setup_dma() {
      dma0->begin(true);              // allocate the DMA channel 
      dma0->TCD->SADDR = &ADC0_RA;    // where to read from
      dma0->TCD->SOFF = 0;            // source increment each transfer
      dma0->TCD->ATTR = 0x101;
      dma0->TCD->NBYTES = 2;     // bytes per transfer
      dma0->TCD->SLAST = 0;
      dma0->TCD->DADDR = &adcbuffer_0[0];// where to write to
      dma0->TCD->DOFF = 2; 
      dma0->TCD->DLASTSGA = -2*BUF_SIZE;
      dma0->TCD->BITER = BUF_SIZE;
      dma0->TCD->CITER = BUF_SIZE;    
      dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma0->disableOnCompletion();    // require restart in code
      dma0->interruptAtCompletion();
      dma0->attachInterrupt(dma0_isr);
      
      dma1->begin(true);              // allocate the DMA channel 
      dma1->TCD->SADDR = &ChannelsCfg_0[0];
      dma1->TCD->SOFF = 2;            // source increment each transfer
      dma1->TCD->ATTR = 0x101;
      dma1->TCD->SLAST = -16;          // num ADC0 samples * 2
      dma1->TCD->BITER = 8;
      dma1->TCD->CITER = 8;
      dma1->TCD->DADDR = &ADC0_SC1A;
      dma1->TCD->DLASTSGA = 0;
      dma1->TCD->NBYTES = 2;
      dma1->TCD->DOFF = 0;
      dma1->triggerAtTransfersOf(*dma0);
      dma1->triggerAtCompletionOf(*dma0);
    
      dma2->begin(true);              // allocate the DMA channel 
      dma2->TCD->SADDR = &ADC1_RA;    // where to read from
      dma2->TCD->SOFF = 0;            // source increment each transfer
      dma2->TCD->ATTR = 0x101;
      dma2->TCD->NBYTES = 2;     // bytes per transfer
      dma2->TCD->SLAST = 0;
      dma2->TCD->DADDR = &adcbuffer_1[0];// where to write to
      dma2->TCD->DOFF = 2; 
      dma2->TCD->DLASTSGA = -2*BUF_SIZE;
      dma2->TCD->BITER = BUF_SIZE;
      dma2->TCD->CITER = BUF_SIZE;    
      dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1);
      dma2->disableOnCompletion();    // require restart in code
      dma2->interruptAtCompletion();
      dma2->attachInterrupt(dma2_isr);
      
      dma3->begin(true);              // allocate the DMA channel 
      dma3->TCD->SADDR = &ChannelsCfg_1[0];
      dma3->TCD->SOFF = 2;            // source increment each transfer
      dma3->TCD->ATTR = 0x101;
      dma3->TCD->SLAST = -8;          // num ADC1 samples * 2
      dma3->TCD->BITER = 4;
      dma3->TCD->CITER = 4;
      dma3->TCD->DADDR = &ADC1_SC1A;
      dma3->TCD->DLASTSGA = 0;
      dma3->TCD->NBYTES = 2;
      dma3->TCD->DOFF = 0;
      dma3->triggerAtTransfersOf(*dma2);
      dma3->triggerAtCompletionOf(*dma2);
    
      dma0->enable();
      dma1->enable();
      
      dma2->enable();
      dma3->enable();
      
    } 
    
    void setup_adc() {
      //ADC0
      //adc->setAveraging(16); // set number of averages
      adc->adc0->setResolution(16); // set bits of resolution
      //adc->setConversionSpeed(ADC_VERY_LOW_SPEED); // change the conversion speed
      //adc->setSamplingSpeed(ADC_HIGH_SPEED); // change the sampling speed
      //adc->adc0->setReference(ADC_REF_1V2);
      
      //ADC1
      //adc->setAveraging(16, ADC_1); // set number of averages
      adc->adc1->setResolution(16); // set bits of resolution
      //adc->setConversionSpeed(ADC_VERY_LOW_SPEED, ADC_1); // change the conversion speed
      //adc->setSamplingSpeed(ADC_HIGH_SPEED, ADC_1); // change the sampling speed
      //adc->adc1->setReference(ADC_REF_1V2);`
      
      ADC1_CFG2 |= ADC_CFG2_MUXSEL;
      
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      adc->adc1->enableDMA();
      
      ADC0_SC1A = ChannelsCfg_0[3];
      ADC1_SC1A = ChannelsCfg_1[3];
    } 
    
    void d2_isr(void) {
      if(debounce > 200){
        d2_active = 1;
        debounce = 0;
        }
        else{return;}
    }
    
    void dma0_isr(void) {
        dma0->TCD->DADDR = &adcbuffer_0[0];
        dma0->clearInterrupt();
        dma0->enable();
        digitalWriteFast(4, HIGH);  //debug
        digitalWriteFast(4, LOW);   //debug
    }
    
    void dma2_isr(void) {
        dma2->TCD->DADDR = &adcbuffer_1[0];
        dma2->clearInterrupt();
        dma2->enable();
        digitalWriteFast(6, HIGH);   //debug
        digitalWriteFast(6, LOW);    //debug
    }

  21. #21
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    No luck so far in figuring out why the adcbuffer_0 and adcbuffer_1 array data is not correctly ordered. (IE 3,0,1,2 instead of 0,1,2,3) using the code I posted from #19.
    I have attempted to changing some of the settings, mostly with worse results or no results at all.

    Is there anyone still following this thread with any ideas to try? I would rather avoid having to work around this bug.

    EDIT...
    On another note I see why so many of my measurements on post 19 are the same, according to ADC_Module.cpp the only usable resolution settings are "For single-ended measurements: 8, 10, 12 or 16 bits.". So if you want say 13 bit you have to set for 16 and shift the results.
    Last edited by Donziboy2; 04-14-2017 at 01:17 PM.

  22. #22
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Quote Originally Posted by Donziboy2 View Post
    No luck so far in figuring out why the adcbuffer_0 and adcbuffer_1 array data is not correctly ordered. (IE 3,0,1,2 instead of 0,1,2,3) using the code I posted from #19.
    The hardware is doing exactly, what you asked it to.

    ADC0_SC1A = ChannelsCfg_0[3];
    This triggers the first conversion, which the last channel. The result is the first DMA transfer.

  23. #23
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    I actually tried changing that and it appears to make no difference what I set it too, even commenting it out makes no difference. I may remove it permanently from the example since it does not appear to do anything in the example.

  24. #24
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Quote Originally Posted by Donziboy2 View Post
    I actually tried changing that and it appears to make no difference what I set it too, even commenting it out makes no difference.
    I guess the ADC conversion finished is asserted anyway, so dma0 is kicked of when you enable the channel.

    The first transfer from dma1 triggers the second conversion (and you already have a result in dma0 from the first one).

    You could for example shift your ChannelsCfg_0 elements, so that they are {ch1, ch2, ch3, ch0}. Or you could make it a DMA circular buffer (the size must be a power of 2) and could initialize the dma1 source pointer with element 1 instead of element 0 (if it's set up as circular buffer, things will properly wrap around).

  25. #25
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Ya, I was hoping I was just doing something silly or it was a known issue :/
    Looks to be something to do with how DMA is initialized. If it ever gets fixed it could play hell with any projects using DMAADC control like this.

    Here is an updated 4/4Channel example with a few more notes and the Channel order changed. I also removed the "ADC0_SC1A = ChannelsCfg_0[3];" and ADC1 lines since they aren't doing anything apparent.

    Code:
    // Converted to only 1 buffer per ADC, reduced to a small example.
    //Based on Example by SaileNav
    
    
    #include "DMAChannel.h"
    #include "ADC.h" 
    
    #define BUF_SIZE 256
    
    ADC *adc = new ADC(); // adc object
    
    DMAChannel* dma0 = new DMAChannel(false);
    DMAChannel* dma1 = new DMAChannel(false);
    DMAChannel* dma2 = new DMAChannel(false);
    DMAChannel* dma3 = new DMAChannel(false);
    
    
    //ChannelsCfg order must be {CH1, CH2, CH3, CH0 }, adcbuffer output will be CH0, CH1, CH2, CH3
    //Order must be {Second . . . . . . . . First} no matter the number of channels used.
    const uint16_t ChannelsCfg_0 [] =  { 0x47, 0x4F, 0x44, 0x46 };  //ADC0: CH0 ad6(A6), CH1 ad7(A7), CH2 ad15(A8), CH3 ad4(A9)
    const uint16_t ChannelsCfg_1 [] =  { 0x45, 0x46, 0x47, 0x44 };  //ADC1: CH0 ad4(A17), CH1 ad5(A16), CH2ad6(A18), CH3 ad7(A19)
    
    const int ledPin = 13;
    
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_0[BUF_SIZE];
    DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer_1[BUF_SIZE];
    
    volatile int d2_active;
    
    void setup() {
      // initialize the digital pin as an output.
      pinMode(ledPin, OUTPUT);
      delay(500); 
    
      d2_active = 0;
      
      pinMode(2, INPUT_PULLUP);
      pinMode(4, OUTPUT);
      pinMode(6, OUTPUT);
    
      attachInterrupt(2, d2_isr, FALLING);
      
      Serial.begin(9600);
      
      // clear buffer
      for (int i = 0; i < BUF_SIZE; ++i){
          adcbuffer_0[i] = 50000;
          adcbuffer_1[i] = 50000;
        
      }
       
      setup_adc();
      setup_dma(); 
      
    }
    elapsedMillis debounce;
    
    
    void loop() {
    
      while(1){     //this is leftover from the original example, it no longer serves a purpose
        uint8_t pin = 0;
        if(d2_active) pin=1;
    
        if(pin>0){
          
          for (int i = 0; i < BUF_SIZE; i = i + 4){
            int a = adcbuffer_0[i];
            Serial.print(a);
            Serial.print(".");
            int b = adcbuffer_0[i+1];
            Serial.print(b);
            Serial.print(".");
            int c = adcbuffer_0[i+2];
            Serial.print(c);
            Serial.print(".");
            int d = adcbuffer_0[i+3];
            Serial.print(d);
            Serial.print("...");                
            int e = adcbuffer_1[i];
            Serial.print(e);        
            Serial.print(".");
            int f = adcbuffer_1[i+1];
            Serial.print(f);        
            Serial.print(".");
            int g = adcbuffer_1[i+2];
            Serial.print(g);        
            Serial.print(".");                        
            int h = adcbuffer_1[i+3];
            Serial.println(h);
    
          }
    
          digitalWrite(ledPin, HIGH);   // set the LED on
          delay(50);                  // wait for a second
          digitalWrite(ledPin, LOW);    // set the LED off
    
          d2_active = 0;     
        }
      }
    
      digitalWrite(ledPin, HIGH);   // set the LED on
      delay(100);                  // wait for a second
      digitalWrite(ledPin, LOW);    // set the LED off
      delay(100);   
    }
    
    void setup_dma() {
      dma0->begin(true);              // allocate the DMA channel 
      dma0->TCD->SADDR = &ADC0_RA;    // where to read from
      dma0->TCD->SOFF = 0;            // source increment each transfer
      dma0->TCD->ATTR = 0x101;
      dma0->TCD->NBYTES = 2;     // bytes per transfer
      dma0->TCD->SLAST = 0;
      dma0->TCD->DADDR = &adcbuffer_0[0];// where to write to
      dma0->TCD->DOFF = 2; 
      dma0->TCD->DLASTSGA = -2*BUF_SIZE;
      dma0->TCD->BITER = BUF_SIZE;
      dma0->TCD->CITER = BUF_SIZE;    
      dma0->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
      dma0->disableOnCompletion();    // require restart in code
      dma0->interruptAtCompletion();
      dma0->attachInterrupt(dma0_isr);
      
      dma1->begin(true);              // allocate the DMA channel 
      dma1->TCD->SADDR = &ChannelsCfg_0[0];
      dma1->TCD->SOFF = 2;            // source increment each transfer (n bytes)
      dma1->TCD->ATTR = 0x101;
      dma1->TCD->SLAST = -8;          // num ADC0 samples * 2
      dma1->TCD->BITER = 4;           // num of ADC0 samples
      dma1->TCD->CITER = 4;           // num of ADC0 samples
      dma1->TCD->DADDR = &ADC0_SC1A;
      dma1->TCD->DLASTSGA = 0;
      dma1->TCD->NBYTES = 2;
      dma1->TCD->DOFF = 0;
      dma1->triggerAtTransfersOf(*dma0);
      dma1->triggerAtCompletionOf(*dma0);
    
      dma2->begin(true);              // allocate the DMA channel 
      dma2->TCD->SADDR = &ADC1_RA;    // where to read from
      dma2->TCD->SOFF = 0;            // source increment each transfer
      dma2->TCD->ATTR = 0x101;
      dma2->TCD->NBYTES = 2;     // bytes per transfer
      dma2->TCD->SLAST = 0;
      dma2->TCD->DADDR = &adcbuffer_1[0];// where to write to
      dma2->TCD->DOFF = 2; 
      dma2->TCD->DLASTSGA = -2*BUF_SIZE;
      dma2->TCD->BITER = BUF_SIZE;
      dma2->TCD->CITER = BUF_SIZE;    
      dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1);
      dma2->disableOnCompletion();    // require restart in code
      dma2->interruptAtCompletion();
      dma2->attachInterrupt(dma2_isr);
      
      dma3->begin(true);              // allocate the DMA channel 
      dma3->TCD->SADDR = &ChannelsCfg_1[0];
      dma3->TCD->SOFF = 2;            // source increment each transfer (n bytes)
      dma3->TCD->ATTR = 0x101;
      dma3->TCD->SLAST = -8;          // num ADC1 samples * 2
      dma3->TCD->BITER = 4;           // num of ADC1 samples
      dma3->TCD->CITER = 4;           // num of ADC1 samples
      dma3->TCD->DADDR = &ADC1_SC1A;
      dma3->TCD->DLASTSGA = 0;
      dma3->TCD->NBYTES = 2;
      dma3->TCD->DOFF = 0;
      dma3->triggerAtTransfersOf(*dma2);
      dma3->triggerAtCompletionOf(*dma2);
    
      dma0->enable();
      dma1->enable();
      
      dma2->enable();
      dma3->enable();
      
    } 
    
    void setup_adc() {
      //ADC0
      //adc->setAveraging(16, ADC_0); // set number of averages
      adc->adc0->setResolution(12); // set bits of resolution
      adc->setConversionSpeed(ADC_MED_SPEED, ADC_0); // change the conversion speed
      adc->setSamplingSpeed(ADC_MED_SPEED, ADC_0); // change the sampling speed
      //adc->adc0->setReference(ADC_REF_1V2);
      
      //ADC1
      //adc->setAveraging(16, ADC_1); // set number of averages
      adc->adc1->setResolution(12); // set bits of resolution
      adc->setConversionSpeed(ADC_MED_SPEED, ADC_1); // change the conversion speed
      adc->setSamplingSpeed(ADC_MED_SPEED, ADC_1); // change the sampling speed
      //adc->adc1->setReference(ADC_REF_1V2);`
      
      ADC1_CFG2 |= ADC_CFG2_MUXSEL;
      
      adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN;  // using software trigger, ie writing to ADC0_SC1A
      adc->adc1->enableDMA();
      
    } 
    
    void d2_isr(void) {
      if(debounce > 200){
        d2_active = 1;
        debounce = 0;
        }
        else{return;}
    }
    
    void dma0_isr(void) {
        dma0->TCD->DADDR = &adcbuffer_0[0];
        dma0->clearInterrupt();
        dma0->enable();
        digitalWriteFast(4, HIGH);
        digitalWriteFast(4, LOW);
    }
    
    void dma2_isr(void) {
        dma2->TCD->DADDR = &adcbuffer_1[0];
        dma2->clearInterrupt();
        dma2->enable();
        digitalWriteFast(6, HIGH);
        digitalWriteFast(6, LOW);
    }
    Last edited by Donziboy2; 12-05-2017 at 11:45 AM. Reason: Typo in ADC setting.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •