Forum Rule: Always post complete source code & details to reproduce any issue!
Page 21 of 21 FirstFirst ... 11 19 20 21
Results 501 to 525 of 525

Thread: ADC library, with support for Teensy 4, 3.x, and LC

  1. #501
    Hi,

    I am aware that for the Teensy 4.0, all analog pins up to A9 can be read by both ADCs. Does anybody know which ADCs can access analog pins A10-A17 on the 4.1?

  2. #502
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,212
    From the core library, looks like A12,A13,A14,A15 are the pins requiring ADC2.

    Code:
    const uint8_t pin_to_channel[] = { // pg 482
            7,      // 0/A0  AD_B1_02
            8,      // 1/A1  AD_B1_03
            12,     // 2/A2  AD_B1_07
            11,     // 3/A3  AD_B1_06
            6,      // 4/A4  AD_B1_01
            5,      // 5/A5  AD_B1_00
            15,     // 6/A6  AD_B1_10
            0,      // 7/A7  AD_B1_11
            13,     // 8/A8  AD_B1_08
            14,     // 9/A9  AD_B1_09
            1,      // 24/A10 AD_B0_12 
            2,      // 25/A11 AD_B0_13
            128+3,  // 26/A12 AD_B1_14 - only on ADC2, 3
            128+4,  // 27/A13 AD_B1_15 - only on ADC2, 4
            7,      // 14/A0  AD_B1_02
            8,      // 15/A1  AD_B1_03
            12,     // 16/A2  AD_B1_07
            11,     // 17/A3  AD_B1_06
            6,      // 18/A4  AD_B1_01
            5,      // 19/A5  AD_B1_00
            15,     // 20/A6  AD_B1_10
            0,      // 21/A7  AD_B1_11
            13,     // 22/A8  AD_B1_08
            14,     // 23/A9  AD_B1_09
            1,      // 24/A10 AD_B0_12
            2,      // 25/A11 AD_B0_13
            128+3,  // 26/A12 AD_B1_14 - only on ADC2, 3
            128+4,  // 27/A13 AD_B1_15 - only on ADC2, 4
    #ifdef ARDUINO_TEENSY41
            255,    // 28
            255,    // 29
            255,    // 30
            255,    // 31
            255,    // 32
            255,    // 33
            255,    // 34
            255,    // 35
            255,    // 36
            255,    // 37
            128+1,  // 38/A14 AD_B1_12 - only on ADC2, 1
            128+2,  // 39/A15 AD_B1_13 - only on ADC2, 2
            9,      // 40/A16 AD_B1_04
            10,     // 41/A17 AD_B1_05
    #endif
    };
    I have some other written notes (not tested code) that A10,A11 are only accessible with ADC1.

  3. #503
    I'm glad i found this! using teensy 4.1 with single reads of several pins assuming 4.1 had "all ADC pins work with all ADCs"
    It was stopping reading when ADC1 read 24/A10 and 25/A11, swapped them to ADC0 and now it is good.

  4. #504
    Junior Member
    Join Date
    Apr 2021
    Posts
    4
    I'm having a problem with "setReference"

    --------

    #include <ADC.h> //ADC support
    ...
    setReference(EXTERNAL); //use external 2.5V ref

    -------

    gives the following compile error:
    'setReference' was not declared in this scope

    with Teensy LC / Arduino IDE v1.8.13/ Teensyduino v1.53

    setReference(REF_EXT); //gives the same error...

    Am I missing something really basic or has a bug crept in? Same results with Teenysduino v1.51 also.

  5. #505
    Senior Member
    Join Date
    Apr 2020
    Location
    DFW area in Texas
    Posts
    304
    Might try the following:

    Code:
       analogReference(EXTERNAL);
    Mark J Culross
    KD5RXT

  6. #506
    Junior Member
    Join Date
    Apr 2021
    Posts
    4
    Quote Originally Posted by kd5rxt-mark View Post
    Might try the following:

    Code:
       analogReference(EXTERNAL);
    Mark J Culross
    KD5RXT
    Mark, thanks that did the trick. How do we fix the documentation?

  7. #507
    Senior Member
    Join Date
    Apr 2020
    Location
    DFW area in Texas
    Posts
    304
    Quote Originally Posted by RJF View Post
    Mark, thanks that did the trick. How do we fix the documentation?
    Glad to be able to help !!

    Sorry, I don't know the answer for your follow-up question. If the error appears on a PJRC website, there's <this thread> where you can report it. If it's some other documentation, Paul will probably need a more specific description of where you found the discrepant info.

    Mark J Culross
    KD5RXT
    Last edited by kd5rxt-mark; 06-18-2021 at 04:02 AM. Reason: fixed typo(s)

  8. #508
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,264
    @pedvide provides the ADC library as a personal external library included with teensyduino: github.com/pedvide/ADC/

    And issues could be made against that github for attention resolution in some fashion.

    Was "REF_3V3" tried per the docs? The REF_EXT may have a different meaning in ADC context ... i.e. an External pin

  9. #509
    Senior Member+
    Join Date
    Jul 2013
    Posts
    294
    Hey, you are using a ADC library method without calling it with `ADC->setReference(ref)`.
    The solution given to use `analogReference` is using a Teensy core library ADC functions, so not using the ADC library.
    See the examples for more information.

  10. #510
    Senior Member
    Join Date
    Jan 2020
    Posts
    216
    I'm using a teensy 4.0. Is there a reason why I'm getting twice the noise if I choose to read certain combinations of analog inputs simultaneously? I'm not quite sure what's going on, I'm reading all sensors, two at a time like this:

    Code:
    void setup() {
    
    ///// ADC0 ////
      adc->adc0->setAveraging(4);                                             // set number of averages
      adc->adc0->setResolution(12);                                           // set bits of resolution
      adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED );       // change the conversion speed
      adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED );           // change the sampling speed
      ////// ADC1 /////
      adc->adc1->setAveraging(4);                                             // set number of averages
      adc->adc1->setResolution(12);                                           // set bits of resolution
      adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED);       // change the conversion speed
      adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED );           // change the sampling speed
    
    }
    
    void loop() {
    
    
    
    
    adc->startSynchronizedSingleRead(A0, A1);
    
    //Here I do some calculations and filtering on the previous two readings
    
      while (adc->adc0->isConverting() || adc->adc1->isConverting());
      g_sensorValue[0] = (adc->adc0->readSingle());
      g_sensorValue[1] = (adc->adc1->readSingle());
      adc->startSynchronizedSingleRead(A2, A3);
      
    //Here I do some calculations and filtering on the previous two readings
    
      
      while (adc->adc0->isConverting() || adc->adc1->isConverting());
      g_sensorValue[2] = (adc->adc0->readSingle());
      g_sensorValue[3] = (adc->adc1->readSingle());
      adc->startSynchronizedSingleRead(A4, A5);
    
      //Here I do some calculations and filtering on the previous two readings
    
     //and so on in the main loop
    It seems I get inconsistent results. When ever I recompile and get more noise I recompile using a different combination of two sensors and usually it cleans up. I haven't noted which combinations work and which don't or if it's completely random.

    Is there a best combination of analog inputs?

    It's not an anomaly, we are talking literally twice the noise. And the noise is 1/f noise.

    Here is a picture showing a filtered analog read of an analog input while at the same time another input is being read but not shown here. The only difference between the left and right on the picture is I changed the other analog read input. So if I for example read A0 and A1 I may get noisy readings on A0 and if I change to A0 and A2 I may get low noise on A0.

    Click image for larger version. 

Name:	noise.jpg 
Views:	57 
Size:	84.6 KB 
ID:	25311
    Last edited by frankzappa; 07-21-2021 at 01:52 PM.

  11. #511
    Senior Member
    Join Date
    Apr 2018
    Location
    Eastern Time, USA
    Posts
    163
    @frankzappa

    As I recall, on the Teensy 3.2 those channel combinations use the same ADC in one case, and different ADC's in the other.

    I notice you have the ADC's set for different speeds. The faster setting on ADC0 might produce more noise whenever you are using that ADC. And it seems plausible that when you use them together for a simultaneous read, you could see different behavior.

    On noise in general, you will see a lot of noise and a lot of cross talk, if you work with hookup wire and hobby board, and through hole boards will usually give you a lot of noise also.

    See this for a link to a note from NXP on noise management,

    https://forum.pjrc.com/threads/54977...y-close-to-it)


    And here is a picture of an SMT board that I built for low noise. You can see some of what is involved.

    Name:  IMG_7161.small.JPG
Views: 300
Size:  7.9 KB
    Last edited by DrM; 07-21-2021 at 01:22 PM.

  12. #512
    Senior Member
    Join Date
    Jan 2020
    Posts
    216
    Quote Originally Posted by DrM View Post
    @frankzappa

    As I recall, on the Teensy 3.2 those channel combinations use the same ADC in one case, and different ADC's in the other.

    I notice you have the ADC's set for different speeds. The faster setting on ADC0 might produce more noise whenever you are using that ADC. And it seems plausible that when you use them together for a simultaneous read, you could see different behavior.

    On noise in general, you will see a lot of noise and a lot of cross talk, if you work with hookup wire and hobby board, and through hole boards will usually give you a lot of noise also.

    See this for a link to a note from NXP on noise management,

    https://forum.pjrc.com/threads/54977...y-close-to-it)


    And here is a picture of an SMT board that I built for low noise. You can see some of what is involved.

    Name:  IMG_7161.small.JPG
Views: 300
Size:  7.9 KB
    No the different speeds is a typo. I'm usig the same speeds.

    It's an SMT board which I designed to the best of my ability. I'm not dissatisfied with the noise I just want it to be consistent.

    What would it be on the board that would give inconsistent results? I'm not sure that it's the switching of analog inputs or if it's just inconsistent?

    Note that this is a very filtered signal and what you see is 1/f noise zoomed in.

    Here is a more common signal:Click image for larger version. 

Name:	signal.jpg 
Views:	43 
Size:	62.2 KB 
ID:	25315

  13. #513
    Senior Member
    Join Date
    Apr 2018
    Location
    Eastern Time, USA
    Posts
    163
    @frankzappa

    Well that is interesting indeed. So the ADC's are configured identically (averaging, speed, amplifier, etc)?

    The engineers at NXP are pretty knowledgeable, it would be a big surprise if the two ADC's had significantly different noise.

    Nonetheless, my next step would be to compare the noise from the same channel using each of the two ADC's. I recall that the library has calls to read an input with a specific ADC, and that there are a few specific channels that can be read by either ADC.


    P/S What do you mean by filtered signal? In circuitry or software? Can we see the unfiltered signals?

  14. #514
    Senior Member
    Join Date
    Jan 2020
    Posts
    216
    Quote Originally Posted by DrM View Post
    @frankzappa

    Well that is interesting indeed. So the ADC's are configured identically (averaging, speed, amplifier, etc)?

    The engineers at NXP are pretty knowledgeable, it would be a big surprise if the two ADC's had significantly different noise.

    Nonetheless, my next step would be to compare the noise from the same channel using each of the two ADC's. I recall that the library has calls to read an input with a specific ADC, and that there are a few specific channels that can be read by either ADC.


    P/S What do you mean by filtered signal? In circuitry or software? Can we see the unfiltered signals?
    Yes same configuration on both channels. The signal I showed is low pass filtered in software. It is a 1.25V DC signal from a voltage reference although you are seeing it as 0. I didn't take a snaphot of the unfiltered signal but it's visible there as well. The noise is the same peak to peak but it switches to the neighboring values much more often.

    You are misunderstanding, the ADC's don't have different noise. They behave differently depending on which two analog inputs are selected. Sometimes if I read 10 sensors, 7 of them can look very clean and the other three are misbehaving. If I switch the order of the ADC acquisitons and switch them around I can maybe get 9 of them to look clean.

    This is the same on breadboards, SMD PCB's, through hole PCB's. I've only tried it on teensy 4.

    It would seem the order of the ADC readings and/or which two are sampled together seems to affect the quality. However I'm not sure it's consistent, I need to write down which analog pairs work well together and test if that's always the case.
    Last edited by frankzappa; 07-21-2021 at 03:43 PM.

  15. #515
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,354
    This will be capacitive coupling issues I think - ADC input sample/hold caps on modern silicon are only a few pF.
    If you want noise-free ADC operation you may have to stop the processor to eliminate wide-band signals banging
    around the chip all the time. The more complex the chip (code excuting, DMA running, IRQs running, peripherals
    active), the more noise is around on the die. If the pins used were only connected to the ADC multiplexer and
    nothing else it might be possible to route them away from crosstalk, but if not its pretty much impossible I suspect.

    Basically an ADC on a microcontroller/microprocessor chip is always quite a compromize. A dedicated ADC chip with
    separate analog and digital supplies and grounds is able to perform way better. But there is a strong business case
    for an on-chip ADC even if its poor, as often this enables a smaller BoM when any old ADC will do...

  16. #516
    Senior Member
    Join Date
    Jan 2020
    Posts
    216
    Quote Originally Posted by MarkT View Post
    This will be capacitive coupling issues I think - ADC input sample/hold caps on modern silicon are only a few pF.
    If you want noise-free ADC operation you may have to stop the processor to eliminate wide-band signals banging
    around the chip all the time. The more complex the chip (code excuting, DMA running, IRQs running, peripherals
    active), the more noise is around on the die. If the pins used were only connected to the ADC multiplexer and
    nothing else it might be possible to route them away from crosstalk, but if not its pretty much impossible I suspect.

    Basically an ADC on a microcontroller/microprocessor chip is always quite a compromize. A dedicated ADC chip with
    separate analog and digital supplies and grounds is able to perform way better. But there is a strong business case
    for an on-chip ADC even if its poor, as often this enables a smaller BoM when any old ADC will do...
    Yeah I figured it would be something like that. It seems to not be consistent. It doesn't matter which two analog inputs I read simultaneously. If I get noisy signals I just have to recompile a few times until I get a compilation when the planets have aligned and the ones and zeros inside the teensy are in order (or something).

  17. #517
    Senior Member
    Join Date
    Jan 2020
    Posts
    216
    I noticed something else. I connected a sensor that I deliberately used a much longer cable so it picks up 60 Hz mains hum, you can see the 60Hz oscillation clearly. When it's misbehaving the 60Hz hum is exactly twice the amplitude. What the hell is going on?

    How can it pick up twice the amount of 60Hz from the long cable?

    As far as I understand an unshileded cable will pick up mains hum from the power in the walls by capacitive coupling. How come it's sometimes twice as susceptible? It's like a switch inside that randomly turns on to mess with you.

  18. #518
    Junior Member
    Join Date
    Oct 2021
    Posts
    1

    explainations !

    Hie,
    Could you explain your code line by line in order to understand the meaning of the different commands ?

    I can guess the global meaning but i 'd like to understand deeper to build my own code for my project.

    Sincerely Yours
    JC



    Quote Originally Posted by KurtE View Post
    @Paul, @Pedvide - Not sure what version of sources are in current beta. Some of the test cases did not compile, I edited one and adc_pdb and when I compared my possible push up to github, it showed more differences... So don't think the current beta release matches github\pedvide master? - Code is using old way to specify speeds...

    Also when I tried the ringbuffer DMA example it did not appear to do any conversions... I have not debugged...

    As I mentioned in the Well monitoring thread, I am playing around with my own version (@hobi not sure if this one will work for you or not).

    When I converted to try to use both ADC0 and ADC1 using DMA and PDB, it worked first time, hung second time. Figured out a fix that got it to work... Wonder if maybe should update ADC stopPDB method to zero out the appropriate(PDB0_CH1C1 or PDB0_CH0C1), doing that allowed it to work multiple times.

    Not sure if anyone will find this version interesting or not... Again WIP...
    Code:
    #include <ADC.h>
    #include <DMAChannel.h>
    #define BUFFER_SIZE 100
    
    ADC adc;
    
    // Variables for ADC0
    volatile DMAMEM uint16_t adc0_buf[BUFFER_SIZE]; // buffer 1...
    volatile uint8_t adc0_busy = 0;
    DMAChannel adc0_dma;
    
    // Variables for ADC1
    volatile DMAMEM uint16_t adc1_buf[BUFFER_SIZE];
    volatile uint8_t adc1_busy = 0;
    DMAChannel adc1_dma;
    
    // References for ISRs...
    extern void adc0_dma_isr(void);
    extern void adc1_dma_isr(void);
    
    
    void setup() {
      while (!Serial && millis() < 3000) ;
      Serial.begin(115200);
      Serial.println("Test DMA Analog Read");
    
      // Initialize the
      adc.setAveraging(4);
      adc.setResolution(12);
      adc.setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED, ADC_0); // change the conversion speed
      adc.setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED, ADC_0); // change the sampling speed
    
    
      adc.setAveraging(4, ADC_1);
      adc.setResolution(12, ADC_1);
      adc.setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED, ADC_1); // change the conversion speed
      adc.setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED, ADC_1); // change the sampling speed
      Serial.printf("First Read of A13: %d\n", adc.analogRead(A13));
      Serial.printf("First Read of A14: %d\n", adc.analogRead(A14));
      Serial.printf("First Read of A15: %d\n", adc.analogRead(A15));
      Serial.printf("First Read of A16: %d\n", adc.analogRead(A16));
    
      // Lets setup Analog 0 dma
      adc0_dma.source((volatile uint16_t&)ADC0_RA);
      adc0_dma.destinationBuffer(adc0_buf, sizeof(adc0_buf));
      adc0_dma.triggerAtHardwareEvent (DMAMUX_SOURCE_ADC0);
      adc0_dma.interruptAtCompletion();
      adc0_dma.disableOnCompletion();
      adc0_dma.attachInterrupt(&adc0_dma_isr);
      NVIC_DISABLE_IRQ(IRQ_PDB); // we don't want or need the PDB interrupt
    
    
      // Lets setup Analog 1 dma
      adc1_dma.source((volatile uint16_t&)ADC1_RA);
      adc1_dma.destinationBuffer(adc1_buf, sizeof(adc1_buf));
      adc1_dma.triggerAtHardwareEvent (DMAMUX_SOURCE_ADC1);
      adc1_dma.interruptAtCompletion();
      adc1_dma.disableOnCompletion();
      adc1_dma.attachInterrupt(&adc1_dma_isr);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      Serial.println("Enter any key to run sample");
      while (Serial.read() == -1) ;
      while (Serial.read() != -1) ;
      Serial.println("Start Sample");
      memset((void*)adc0_buf, 0, sizeof(adc0_buf));
      memset((void*)adc1_buf, 0, sizeof(adc1_buf));
      uint32_t start_time = millis();
    
      // Start 0
      adc0_busy = 1;
      adc1_busy = 1;
      adc.adc0->startSingleRead(A14);
      adc.adc1->startSingleRead(A13);
      adc0_dma.enable();
      adc1_dma.enable();
      adc.enableDMA(ADC_0);
      adc.enableDMA(ADC_1);
      adc0_dma.enable();
      adc1_dma.enable();
      adc.adc0->startPDB(60 * 50);
      adc.adc1->startPDB(60 * 50);
    
      dumpDMA_TCD("ADC_0", &adc0_dma);
      dumpDMA_TCD("ADC_1", &adc1_dma);
    
      while (adc0_busy || adc1_busy) {
        if ((millis() - start_time) > 1000) {
          Serial.printf("Timeout %d %d\n", adc0_busy, adc1_busy);
          break;
        }
      }
      
      PDB0_CH1C1 = 0;
      PDB0_CH0C1 = 0;
    
      adc.adc0->stopPDB();
      adc0_dma.disable();
      adc.disableDMA(ADC_0);
      adc0_dma.disable();
    
      adc.adc1->stopPDB();
      adc.adc1->stopPDB();
      adc1_dma.disable();
      adc.disableDMA(ADC_1);
    
      dumpDMA_TCD("ADC_0", &adc0_dma);
      dumpDMA_TCD("ADC_1", &adc1_dma);
    
      Serial.printf("Sample completed: %d\n", millis() - start_time);
      for (int i = 0; i < BUFFER_SIZE; i++) {
        Serial.printf("%4u ", adc0_buf[i]);
        if ((i % 20) == 19) Serial.println();
      }
      Serial.println("\n");
    
      for (int i = 0; i < BUFFER_SIZE; i++) {
        Serial.printf("%4u ", adc1_buf[i]);
        if ((i % 20) == 19) Serial.println();
      }
      Serial.println();
    
    
    }
    
    
    void adc0_dma_isr(void)
    {
      adc0_busy = false;
      adc0_dma.clearInterrupt();
      adc0_dma.clearComplete();
    }
    
    void adc1_dma_isr(void)
    {
      adc1_busy = false;
      adc1_dma.clearInterrupt();
      adc1_dma.clearComplete();
    }
    
    
    
    typedef struct  __attribute__((packed, aligned(4))) {
      uint32_t SADDR;
      int16_t SOFF;
      uint16_t ATTR;
      uint32_t NBYTES;
      int32_t SLAST;
      uint32_t DADDR;
      int16_t DOFF;
      uint16_t CITER;
      int32_t DLASTSGA;
      uint16_t CSR;
      uint16_t BITER;
    } TCD_DEBUG;
    
    void dumpDMA_TCD(const char *psz, DMABaseClass *dmabc)
    {
      Serial.printf("%s %08x %08x:", psz, (uint32_t)dmabc, (uint32_t)dmabc->TCD);
      TCD_DEBUG *tcd = (TCD_DEBUG*)dmabc->TCD;
      Serial.printf("%08x %04x %04x %08x %08x ", tcd->SADDR, tcd->SOFF, tcd->ATTR, tcd->NBYTES, tcd->SLAST);
      Serial.printf("%08x %04x %04x %08x %04x %04x\n", tcd->DADDR, tcd->DOFF, tcd->CITER, tcd->DLASTSGA,
                    tcd->CSR, tcd->BITER);
    
    }
    Adding the lines in RED was what got it to work multiple times... Now back to next part of what I mentioned in well monitor...

  19. #519
    Senior Member
    Join Date
    Oct 2012
    Location
    Portland OR
    Posts
    712
    If both the noise and the 60 Hz pickup change by a factor of 2, that sounds like the different sampling configurations are somehow either changing the effective impedance of the ADC input at those frequencies, or simply the actual overall gain of the channel. Is it possible there is a single-ended vs. differential mode being used? Seems like there should be a way to measure that systematically.

  20. #520
    Junior Member
    Join Date
    Nov 2021
    Posts
    4
    I succeeded in setting up the ADC + DMA library example on a Teensy 4.1 for continuous conversion of a single pin. Is it possible to extend that to multiple pins?

    I come from ST microcontrollers and there it is pretty common to have an adc buffer with length = number of pins to measure and let the DMA transfer the ADCs results continuously. That way you can access the latest ADC measurements whenever you want in your code without need to worry about timing.

    Thank you for any hint

  21. #521
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    9,728
    Quote Originally Posted by tompilot View Post
    I succeeded in setting up the ADC + DMA library example on a Teensy 4.1 for continuous conversion of a single pin. Is it possible to extend that to multiple pins?
    ...
    Thank you for any hint [/QUOTE]

    Possible? Yes. Is the support in the library? I don't think so.
    Would it be hard to hack it up... Probably not...

    Look at the example adc_timer_dma.ino

    Code:
    #if defined(__IMXRT1062__)
    
      Serial.println("\n*** ADC and ADC_ETC ***");
      Serial.printf("ADC1: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", IMXRT_ADC1.HC0, IMXRT_ADC1.HS,  IMXRT_ADC1.CFG, IMXRT_ADC1.GC, IMXRT_ADC1.GS);
      Serial.printf("ADC2: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", IMXRT_ADC2.HC0, IMXRT_ADC2.HS,  IMXRT_ADC2.CFG, IMXRT_ADC2.GC, IMXRT_ADC2.GS);
      Serial.printf("ADC_ETC: CTRL:%x DONE0_1:%x DONE2_ERR:%x DMA: %x\n", IMXRT_ADC_ETC.CTRL,
                    IMXRT_ADC_ETC.DONE0_1_IRQ, IMXRT_ADC_ETC.DONE2_ERR_IRQ, IMXRT_ADC_ETC.DMA_CTRL);
      for (uint8_t trig = 0; trig < 8; trig++) {
        Serial.printf("    TRIG[%d] CTRL: %x CHAIN_1_0:%x\n",
                      trig, IMXRT_ADC_ETC.TRIG[trig].CTRL, IMXRT_ADC_ETC.TRIG[trig].CHAIN_1_0);
      }
    #endif
    Notice the .CHAIN_1_0...
    To understand these you need to look at the Reference Manual ADC_ETC chapter (67).
    You will see there are 4 chain registers, each can handle 2 pins, (The pins need to be defined as the ADC pin number... And if I remember correctly there is another register
    that will say how many in the chain.

    When setup if I remember correctly, the DMA output, will intermix all of the results in the same DMA stream, one after another.....

  22. #522
    Junior Member
    Join Date
    Nov 2021
    Posts
    4
    Thank you, sounds good! Will give it a try.

  23. #523
    Junior Member
    Join Date
    Nov 2021
    Posts
    4
    Okay, i am getting closer after digging into chapter 67.
    What i tried to do:
    Based on the example "adc_timer_dma" i try to make the (Quad-)Timer trigger the ADC conversion of ADC1 and ADC2.
    ADC0 is set up as the library does with only one Pin (= chain length 1) with Teensy Pin 14 = ADC0_NR 7
    ADC1 is set up as a chain of length 2 with Teensy Pin 40 has ADC1_NR 9 and Teensy Pin 27 has ADC1_NR 4

    But only ADC0 throws interrupts, i don't know if ADC1 actually runs.
    Below is the result of print_debug_information().
    I am not sure what the settings of ADC2:HC0 should be...

    Code:
    *** DMA structures for ADC_0 ***
    2004d6a0 400e9000:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200002 DO: 2 CI:7 DL:2004d680 CS:12 BI:8
    2004d620 2004d640:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200000 DO: 2 CI:8 DL:2004d680 CS:12 BI:8
    2004d660 2004d680:SA:400c4024 SO:0 AT:101 NB:2 SL:0 DA:20200020 DO: 2 CI:8 DL:2004d640 CS:12 BI:8
    
    *** DMA structures for ADC_1 ***
    2004d760 400e9020:SA:400c8024 SO:0 AT:101 NB:2 SL:0 DA:20200042 DO: 2 CI:7 DL:2004d740 CS:12 BI:8
    2004d6e0 2004d700:SA:400c8024 SO:0 AT:101 NB:2 SL:0 DA:20200040 DO: 2 CI:8 DL:2004d740 CS:12 BI:8
    2004d720 2004d740:SA:400c8024 SO:0 AT:101 NB:2 SL:0 DA:20200060 DO: 2 CI:8 DL:2004d700 CS:12 BI:8
    
    *** ADC and ADC_ETC ***
    ADC1: HC0:10 HS:0 CFG:16278 GC:22 GS:0
    ADC2: HC0:10 HS:0 CFG:16278 GC:22 GS:0
    ADC_ETC: CTRL:20000011 DONE0_1:0 DONE2_ERR:0 DMA: 11
        TRIG[0]_CTRL: 0 CHAIN_1_0: 2017
        TRIG[1]_CTRL: 0 CHAIN_1_0:0
        TRIG[2]_CTRL: 0 CHAIN_1_0:0
        TRIG[3]_CTRL: 0 CHAIN_1_0:0
        TRIG[4]_CTRL: 100 CHAIN_1_0: 2004 1019
        TRIG[5]_CTRL: 0 CHAIN_1_0:0
        TRIG[6]_CTRL: 0 CHAIN_1_0:0
        TRIG[7]_CTRL: 0 CHAIN_1_0:0
    The interesting part is probably:

    * this works *
    TRIG[0]_CTRL: 0 CHAIN_1_0: 2017
    TRIG chain length = 1, no Sync mode, lowest priority, hardware trigger
    Chain 0:
    - Interrupt on Done0
    - Hardware trigger 0 (timer overflow creates a hardware trigger?)
    - ADC channel 7 selected
    Chain 1: not used

    * this does not create interrupts (or work at all) *
    TRIG[4]_CTRL: 100 CHAIN_1_0: 2004 1019
    Chain 0:
    - No interrupt
    - B2B back-to-back enabled
    - HW trigger 0
    - ADC channel 9
    Chain 1:
    - Interrupt on Done0
    - B2B disabled
    - No hardware trigger
    - ADC channel 4

  24. #524
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,655
    Sorry for not seeing this sooner @KurtE pointed me too it. Back in the T4 test days played a lot with ADC to get chaining to work. Put together a sketch (non-DMA) that sets up the proper configuration and interrupts to get it working.

    ADCLpitxbaradcV8.zip

    The sketch uses both ADC0 and ADC1. In the adc_etc_setup.ino file has the interesting bits on configuration for chaining and setting up the ISRs. For instance:

    Code:
    void adc0_etc_ctrl_init(uint8_t chainLength_0,
                      int adc_0_pinArray[], 
                      uint8_t trigger_adc_0, bool DMA) 
    {
      int numPins_adc_0 = sizeof(adc_0_pinArray);
      uint8_t adc_pin_channel, res;
    
      delay(5);
      
      // Now lets process ADC1(ADC_0) pin...
      
      for(uint8_t i = 0; i < numPins_adc_0; i++){
        // check pins
        if ( !adc->adc0->checkPin(adc_0_pinArray[i]) ) {
            Serial.printf("ADC_ETC_INIT: pin:%d did not map to channnel on %d\n", adc_0_pinArray[i], ADC_0);
            adc->adc0->fail_flag |= ADC_ERROR::WRONG_PIN;
            return res;
        }
        //adc_pin_channel = mapPinToChannel(adc_0_pinArray[i], ADC_0);
        //if (adc_pin_channel == 0xff) 
        //  Serial.printf("ADC_ETC_INIT: pin:%d did not map to channnel on %d\n", adc_0_pinArray[i], ADC_0);
      }
      IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1<<trigger_adc_0)); // 0x40000001;  // start with trigger 0
      IMXRT_ADC_ETC.TRIG[trigger_adc_0].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength_0-1);
    
      if(DMA)
        IMXRT_ADC_ETC.DMA_CTRL = ADC_ETC_DMA_CTRL_TRIQ_ENABLE(trigger_adc_0);
      Serial.printf("ADC1: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC1_HC0, ADC1_HS,  ADC1_CFG, ADC1_GC, ADC1_GS);
      Serial.printf("ADC_ETC: CTRL:%x DMA: %x TRIG0: CTRL: %x CHAIN01:%x\n",
                    IMXRT_ADC_ETC.CTRL, IMXRT_ADC_ETC.DMA_CTRL, ADC_ETC_TRIG0_CTRL, ADC_ETC_TRIG0_CHAIN_1_0);
    
    }
    the function takes as input the chain length = number of adc pins which is read in via the pin array from the main sketch:
    Code:
    int adc_0_pins[] = {14, 15, 16, 17};
    uint8_t trigger_adc_0 = 0;
    As well as the trigger pin (trigger_adc_0) and a boolean if you want to config it to use DMA.

    There are a few more things like setting it up for a timer interrupt. Enjoy - hope it helps

  25. #525
    Junior Member
    Join Date
    Nov 2021
    Posts
    4
    Thank you so much for your input! I got the chain to work without DMA.
    The key was to set the CHAIN_X_Y registers correctly.
    Therefore i made a modified version of the library function "startQuadTimer":

    Code:
    void ADC_Module::startQuadTimer(uint32_t freq)
    {
        // Update the ADC
        uint8_t adc_pin_channel = adc_regs.HC0 & 0x1f; // remember the trigger that was set 
        uint8_t arr[1];
        arr[0] = adc_pin_channel;
        startQuadTimer(freq, 1, arr, false);
    }
    
    
    void ADC_Module::startQuadTimer(uint32_t freq, uint8_t nrOfPins, uint8_t* pinArray, bool pinsAreTeensyPins)
    {  
        if(pinsAreTeensyPins) {
            for (uint8_t k=0; k<nrOfPins; k++) {
                Serial.printf("startQuadTimer: Teensy Pin %i has ADC%i_NR ", pinArray[k], ADC_num);
                pinArray[k] = channel2sc1a[pinArray[k]];
                Serial.println(pinArray[k]);
            }
        }  
        Serial.printf("startQuadTimer: ADC_ETC_TRIGGER_INDEX = %i", ADC_ETC_TRIGGER_INDEX);
        Serial.println();
    
        // First lets setup the XBAR
        CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); //turn clock on for xbara1
        xbar_connect(XBAR_IN, XBAR_OUT);
    
        // Update the ADC
        // uint8_t adc_pin_channel = adc_regs.HC0 & 0x1f; // remember the trigger that was set 
        setHardwareTrigger();                          // set the hardware trigger
        adc_regs.HC0 = (adc_regs.HC0 & ~0x1f) | 16;    // trigger only by ADC_ETC channel remember other states...
        singleMode();                                  // make sure continuous is turned off as you want the trigger to di it.
    
        // 
        Serial.printf("startQuadTimer: adc_regs.HC0 = 0x");
        Serial.println(adc_regs.HC0, 16);
    
        // setup adc_etc - BUGBUG have not used the preset values yet.
        if (IMXRT_ADC_ETC.CTRL & ADC_ETC_CTRL_SOFTRST)
        { // SOFTRST
            // Soft reset - ORIGINAL from lib
            //atomic::clearBitFlag(IMXRT_ADC_ETC.CTRL, ADC_ETC_CTRL_SOFTRST);
            // Soft reset from PJRC forum
            IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
            IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
            delay(5); // give some time to be sure it is init
        }
        if (ADC_num == 0)
        { // BUGBUG - in real code, should probably know we init ADC or not..
            // what is this Bypass thing?
            IMXRT_ADC_ETC.CTRL |= (ADC_ETC_CTRL_TSC_BYPASS | ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX)); // 0x40000001;  // start with trigger 0
            //IMXRT_ADC_ETC.CTRL = (ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX)); // 0x40000001;  // start with trigger 0
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(nrOfPins-1);   // TH: chain length // chainlength -1 only us
        }
        else
        {
            // This is our second one... Try second trigger?
            // what is this Bypass thing? Manual says clear that bit on ADC2
            IMXRT_ADC_ETC.CTRL &= ~(ADC_ETC_CTRL_TSC_BYPASS); // 0x40000001;  // start with trigger 0
            IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_DMA_MODE_SEL | ADC_ETC_CTRL_TRIG_ENABLE(1 << ADC_ETC_TRIGGER_INDEX); // Add trigger
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(nrOfPins-1);   // TH: chain length // chainlength -1 only us
        }
        if(nrOfPins == 1) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 = ADC_ETC_TRIG_CHAIN_IE0(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]); /*| ADC_ETC_TRIG_CHAIN_B2B0 */
        } else if (nrOfPins == 2) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
        } else if (nrOfPins == 3) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_3_2 |= 
            ADC_ETC_TRIG_CHAIN_IE0(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[2]) | ADC_ETC_TRIG_CHAIN_B2B0;
        } else if (nrOfPins == 4) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_3_2 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[2]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[3]) | ADC_ETC_TRIG_CHAIN_B2B1;
        } else if (nrOfPins == 5) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_3_2 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[2]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[3]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_5_4 |= 
            ADC_ETC_TRIG_CHAIN_IE0(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[4]) | ADC_ETC_TRIG_CHAIN_B2B0;
        } else if (nrOfPins == 6) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_3_2 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[2]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[3]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_5_4 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[4]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[5]) | ADC_ETC_TRIG_CHAIN_B2B1;
        } else if (nrOfPins == 7) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_3_2 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[2]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[3]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_5_4 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[4]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[5]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_7_6 |= 
            ADC_ETC_TRIG_CHAIN_IE0(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[6]) | ADC_ETC_TRIG_CHAIN_B2B0;
        } else if (nrOfPins >= 8) {
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_1_0 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[0]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[1]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_3_2 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[2]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[3]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_5_4 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[4]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(0) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[5]) | ADC_ETC_TRIG_CHAIN_B2B1;
            IMXRT_ADC_ETC.TRIG[ADC_ETC_TRIGGER_INDEX].CHAIN_7_6 |= 
            ADC_ETC_TRIG_CHAIN_IE0(0) | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(pinArray[6]) | ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE1(ADC_num+1) | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(pinArray[7]) | ADC_ETC_TRIG_CHAIN_B2B1;
        }
    
        if (interrupts_enabled)
        {
            // Not sure yet?
        }
        if (adc_regs.GC & ADC_GC_DMAEN)
        {
            IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(ADC_ETC_TRIGGER_INDEX);
        }
    
        // Now init the QTimer.
        // Extracted from quadtimer_init in pwm.c but only the one channel...
        // Maybe see if we have to do this every time we call this.  But how often is that?
        IMXRT_TMR4.CH[QTIMER4_INDEX].CTRL = 0; // stop timer
        IMXRT_TMR4.CH[QTIMER4_INDEX].CNTR = 0;
        IMXRT_TMR4.CH[QTIMER4_INDEX].SCTRL = TMR_SCTRL_OEN | TMR_SCTRL_OPS | TMR_SCTRL_VAL | TMR_SCTRL_FORCE;
        IMXRT_TMR4.CH[QTIMER4_INDEX].CSCTRL = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_ALT_LOAD;
        // COMP must be less than LOAD - otherwise output is always low
        IMXRT_TMR4.CH[QTIMER4_INDEX].LOAD = 24000; // low time  (65537 - x) -
        IMXRT_TMR4.CH[QTIMER4_INDEX].COMP1 = 0;    // high time (0 = always low, max = LOAD-1)
        IMXRT_TMR4.CH[QTIMER4_INDEX].CMPLD1 = 0;
        IMXRT_TMR4.CH[QTIMER4_INDEX].CTRL = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) |
                                            TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(6);
    
        quadtimerFrequency(&IMXRT_TMR4, QTIMER4_INDEX, freq);
        quadtimerWrite(&IMXRT_TMR4, QTIMER4_INDEX, 5);
    }
    Anyways, when using a DMA, the DMA buffer does not always start with the same ADC value. My buffer length is equal to the nr of pins i am measuring and i also see all the values correctly, but the start index in the DMA buffer changes.
    I am practicacally using the library example adc_timer_dma.ino with my modified startQuadTimer function.
    I also noticed, that:
    - pabdma->interruptDeltaTime() does not correspond to the timer interval, instead it is always shorter by roughly factor 2
    - the buffer length changes the frequency of DMA interrupts (= debug output in my case), smaller buffer means higher interrupt rate
    - Done0/1/2 settings in the chain registers do not affect the DMA triggering at all (which is expected as the DMA seems to be linked to the trigger source of the ADC chain...)

    Code:
    void setup() {
    const uint32_t buffer_size = 5;
    DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff1[buffer_size];
    DMAMEM static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff2[buffer_size];
    AnalogBufferDMA abdma1(dma_adc_buff1, buffer_size, dma_adc_buff2, buffer_size);
    
    adc->adc0->setAveraging(32); // set number of averages
    adc->adc0->setResolution(12); // set bits of resolution
    abdma1.init(adc, ADC_0);
    adc->adc0->startTimer(100, 5, potPins, true); // This uses my modified function from above, start timer at 100 Hz, sample an array (potPins) of length 5
    }
    
    void loop() {
      if ( abdma1.interrupted()) {
        ProcessAnalogData(&abdma1, 0);
      }
    }
    
    void ProcessAnalogData(AnalogBufferDMA *pabdma, int8_t adc_num) {
    
      volatile uint16_t *pbuffer = pabdma->bufferLastISRFilled();
      if ((uint32_t)pbuffer >= 0x20200000u)  arm_dcache_delete((void*)pbuffer, sizeof(dma_adc_buff2_1));
    
      Serial.printf("interruptDeltaTime: %i ms    ", pabdma->interruptDeltaTime());
      for(uint8_t i=0; i<5; i++) {
          Serial.printf("%i     ", pbuffer[i]);
      }
      Serial.println();
      pabdma->clearInterrupt();
    }

Posting Permissions

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