Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: Problem using micros() at qtimer triggered ADC_ETC above 2.3kHz

  1. #1
    Junior Member
    Join Date
    Jun 2021
    Posts
    6

    Problem using micros() at qtimer triggered ADC_ETC above 2.3kHz

    To validate the samplerate from the teensy 4 ADC's I wanted to timestamp each time an ADC_ETC_DONE interrupt was triggered using micros(). This works up to about 2.3kHz, at higher frequencies micro(s) returns wrong values >50% of the times. I know that micro(s) can be called a lot more frequently inside of adcetc0_isr() below. I have tested the code with just the time stamping inside of adcetc0_isr, but the same still occurs. Is there some codepency with QTimer4_X and micros() ?
    I have looked into pwm.c and how the TMRx_CTRLn registers are set but did not see anything that could cause it. Is it better to forget this idea and use a signal generator to check if the sampling rate is still in accordance with the QTIMER4 frequency above 2.3kHz?

    The code was based on the work of @mjs513 (ADCLpitxbadacV7) https://forum.pjrc.com/threads/58387...l=1#post224915. I store the data in circular buffers (tonton81 https://github.com/tonton81/Circular_Buffer)
    I have changed the code somewhat for my purpose, for instance only getting an interrupt when the final pin/channel of the triggerchain has been converted and using a dedicated IRQ for each ADC.


    My code (use it inside of Visual Studio Code/Platform.io )

    Code:
    #include <Arduino.h>
    
    #include <ADC.h>
    #include <circular_buffer.h>
    
    
    #define USE_TIMED_READS
    int adc_0_pins[] = {A0, A1, A2, A3, A4, A5};
    int adc_1_pins[] = {A6, A7, A8, A9,A10,A11};
    const uint8_t channels = 6;
    
    
    const uint8_t trigger_adc_0 = 0; // linked to adc_0
    const uint8_t trigger_adc_1 = 4; // linked to adc_1
    
    //uint8_t chain_len_0 = 6; // chain length max 8
    const uint8_t chain_len_0 = 6; // chain length max 8, also number of items 
    const uint8_t chain_len_1 = 6;
    
    float freqsamp_1 = 2000.0;
    float freqsamp_2 = 2000.0;
    
    const uint32_t buflen_0 = 640; // buffer length adc 1
    const uint32_t buflen_1 = 256; // buffer length adc 2
    
    
    ADC *adc = new ADC(); // adc object
    
    #define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)
    volatile uint32_t val0, val1;
    
    //debug parameters
    uint32_t t1; // start time;
    uint32_t t2; // end time;
    int32_t dt; // elapsed time;
    
    //timestamp vars
    const uint32_t n_stamps = 200;
    uint32_t stnr = 0 ; // time array index
    bool printing = true; 
    bool first = true;
    
    
    // uint32_t time_st[n_stamps];
    
    uint8_t ch_adc0 = 0; // adc0 channel index
    uint8_t ch_adc1 = 0; // adc1 channel index 
    
    // connect parameters
    uint8_t XBAR_IN;
    uint8_t XBAR_OUT;
    
    uint8_t priority = 16; // interrupt priority 0-255, low numbers= high priority
    
    //uint8_t QTIMER4_INDEX;
    //int8_t ADC_ETC_TRIGGER_INDEX;
    
    
    // declaration of functions
    void setup_adc_hardware_trigger();
    void adc_init();
    void adc_etc_init();
    void connectTimer(uint8_t ADC_num);
    void startTimer(uint8_t timer, float freq);
    void adcetc0_isr();
    void adcetc1_isr();
    
    void adc_etc_reset();
    void adc0_etc_ctrl_init(uint8_t chainLength_0,
                            int adc_0_pinArray[],
                            uint8_t trigger_adc_0, bool DMA);
    void adc1_etc_ctrl_init(uint8_t chainLength_1, 
                            int adc_1_pinArray[],
                            uint8_t trigger_adc_1, bool DMA);
    void ADC_ETC_SetTriggerChainConfig(uint8_t adc_num,
                                       uint8_t done,
                                       uint32_t triggerGroup,
                                       uint32_t chainGroup, //0, 1, 2, 3 (0_1, 3_2....
                                       uint8_t pin1, uint8_t pin2);
    uint16_t ADC_ETC_GetADCConversionValue(uint32_t triggerGroup, uint32_t chainGroup);
    uint8_t mapPinToChannel(uint8_t pin, int8_t adc_num);
    
    
    
    // Assign circular buffers 
    // circularbuffer setup ADC_0 <datatype , slots, items 
    Circular_Buffer<uint16_t,buflen_0> rb_adc_0[6];
    
    Circular_Buffer<uint16_t,buflen_1> rb_adc_1[6];
    
    Circular_Buffer<int32_t,buflen_0> timest;
    
    
    void setup() { 
    
      while (!Serial && millis() < 5000) ;
    
      //pinMode(LED_BUILTIN, OUTPUT);
       
       for (int i = 0; i < channels; i++)
        {
            pinMode(adc_0_pins[i], INPUT);
            pinMode(adc_1_pins[i], INPUT);
        }
    
      Serial.begin(9600);
      Serial.println("Setup ADC_0");
    
      // adc settings;
      // averaging options 1/4/8/16/32
      // resolution 8/10/12  ADCK cycles: 17/21/25
      adc->adc0->setAveraging(1); // set number of averages
      adc->adc0->setResolution(10); // set bits of resolution
      adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // ADCK at 40MHz
      adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // adds 10 ADCK cycles, impedance dependant
      adc->adc0->singleMode();
      //adc->adc0->startQuadTimer();
    
      adc->adc1->setAveraging(4); // set number of averages
      adc->adc1->setResolution(10);
      adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::HIGH_SPEED); // ADCK at 40MHz
      adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // adds 10 ADCK cycles, impedance dependant                     
      adc->adc1->singleMode();
    
      // always call the compare functions after changing the resolution!
      //adc->enableCompare(1.0/3.3*adc->getMaxValue(ADC_0), 0, ADC_0); // measurement will be ready if value < 1.0V
      //adc->enableCompareRange(1.0*adc->getMaxValue(ADC_1)/3.3, 2.0*adc->getMaxValue(ADC_1)/3.3, 0, 1, ADC_1); // ready if value lies out of [1.0,2.0] V
    
      setup_adc_hardware_trigger();
     
      Serial.println("End Setup");
    
    }
    
    void loop() {
    //  Serial.printf("ADC0:  %d  %d  %d  %d  %d  %d\n", rb_adc_0[0].peek(0), rb_adc_0[1].peek(0), rb_adc_0[2].peek(0), 
    //  rb_adc_0[3].peek(0), rb_adc_0[4].peek(0), rb_adc_0[5].peek(0));
    //  Serial.printf("ADC1:  %d  %d  %d  %d  %d  %d\n", rb_adc_1[0].peek(0), rb_adc_1[1].peek(0), rb_adc_1[2].peek(0), 
    //  rb_adc_1[3].peek(0), rb_adc_1[4].peek(0), rb_adc_1[5].peek(0));
    //  delay(100);
    //  for (uint8_t i = 0; i < 10; i++){
    //   Serial.printf("%d \n",timest[i]);
    //  }
     //delay(100); // time to type 'pio device monitor > log.txt' in the PlatformIO CLI terminal  
      
      delay(10000);
      if(printing) {
        uint16_t buffer0_1 [64]; 
        int32_t printstamps[64]; // printbuffer filled from timestamps ringbuffer
        timest.readBytes(printstamps,64);
        rb_adc_0[1].readBytes(buffer0_1,64);
        for (uint8_t i=0; i < 64;i++){
          Serial.printf("%d %d\n",printstamps[i]),buffer0_1[i];
          delayMicroseconds(2);
        }
        printing = false;
      }
     }
    
    void setup_adc_hardware_trigger()
    {
      adc_init();
      adc_etc_init();
      Serial.println("After ADC/ADC_ETC"); Serial.flush();
    
      //timer 1 = QTIMER4_0 / timer2=QTIMER4_3 / PIT timer
      connectTimer(1); //connect QTIMER to ADC_ETC with XBAR 
      connectTimer(0); //connect QTIMER to ADC_ETC with XBAR
      Serial.println("After XBAR"); Serial.flush();
     
      startTimer(1, freqsamp_1);   // 1 khz  1000 ADCs/sec
      startTimer(2, freqsamp_2);  // try at 20 hz for test...
    }
    
    void adc_init() {
      ADC1_CFG |= ADC_CFG_ADTRG;   // hardware trigger
      ADC1_HC0 = 16;   // ADC_ETC channel, 144 int enabled, 16 int disabled
      ADC1_GC &= ~ADC_GC_ADCO; // continuous conversion disabled, same as adc->adcX->singleMode()
      
      ADC2_CFG |= ADC_CFG_ADTRG;   // hardware trigger
      ADC2_HC0 = 16;   // ADC_ETC channel
      ADC2_GC &= ~ADC_GC_ADCO; // continuous conversion disabled 
    }
    
    void adc_etc_init() {
    
      adc_etc_reset();
    
      adc0_etc_ctrl_init(chain_len_0,         //chainLength_0, chainLength_1
                         adc_0_pins, //*adc_0_pinArray, *adc_1_pinArray
                         trigger_adc_0,          //trigger_adc_0, trigger_adc_1
                         false);     //bool DMA,
      adc1_etc_ctrl_init(chain_len_1,         //chainLength_0, chainLength_1
                         adc_1_pins, //*adc_0_pinArray, *adc_1_pinArray
                         trigger_adc_1,          //trigger_adc_0, trigger_adc_1
                         false);     //bool DMA
      //  ADC_ETC_SetTriggerChainConfig(ADC,done#, trigger#,  channel Group#, pin1, pin2);
      // continue the chain with pairs of 2
        
      ADC_ETC_SetTriggerChainConfig(ADC_0, 1, trigger_adc_0, 0, adc_0_pins[0], adc_0_pins[1]);
      ADC_ETC_SetTriggerChainConfig(ADC_0, 1, trigger_adc_0, 1, adc_0_pins[2], adc_0_pins[3]);
      ADC_ETC_SetTriggerChainConfig(ADC_0, 1, trigger_adc_0, 2, adc_0_pins[4], adc_0_pins[5]);
      ADC_ETC_SetTriggerChainConfig(ADC_1, 2, trigger_adc_1, 0, adc_1_pins[0], adc_1_pins[1]);
      ADC_ETC_SetTriggerChainConfig(ADC_1, 2, trigger_adc_1, 1, adc_1_pins[2], adc_1_pins[3]);
      ADC_ETC_SetTriggerChainConfig(ADC_1, 2, trigger_adc_1, 2, adc_1_pins[4], adc_1_pins[5]);
    
      // attach ADC_ETC0 IRQ to ADC1
      attachInterruptVector(IRQ_ADC_ETC0, adcetc0_isr);
      //attachInterruptVector(IRQ_ADC_ETC0, timestamp);
      NVIC_SET_PRIORITY(IRQ_ADC_ETC0, priority);
      NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
      attachInterruptVector(IRQ_ADC_ETC1, adcetc1_isr);
      NVIC_SET_PRIORITY(IRQ_ADC_ETC1, priority);
      NVIC_ENABLE_IRQ(IRQ_ADC_ETC1);
      //Maakt het uit dat ik nu IRQ_ADC_ETC0 verbind met beiden, dat staat
      // toch haaks op het idee dat ik twee onafhankelijke trigger frequenties heb?
      //attachInterruptVector(IRQ_ADC_ETC0, adcetc1_isr);
      //NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
      //attachInterruptVector(IRQ_ADC_ETC0, adcetc1_isr);
      //NVIC_ENABLE_IRQ(IRQ_ADC_ETC0);
    }
    
    // modified version from T4 analog.c
    // 0x80 bit - Only on ADC2
    // 0x40 bit - only on ADC1
    const uint8_t  t4_pin_to_channel[] = {
      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
      0x40 + 1, // 24/A10 AD_B0_12
      0x40 + 2, // 25/A11 AD_B0_13
      0x80 + 3, // 26/A12 AD_B1_14 - only on ADC2, 3
      0x80 + 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
      0x40 + 1, // 24/A10 AD_B0_12
      0x40 + 2, // 25/A11 AD_B0_13
      0x80 + 3, // 26/A12 AD_B1_14 - only on ADC2, 3
      0x80 + 4 // 27/A13 AD_B1_15 - only on ADC2, 4
    };
    
    
    #ifdef USE_TIMED_READS
    // try to use some teensy core functions...
    // mainly out of pwm.c
    extern "C" {
      extern void xbar_connect(unsigned int input, unsigned int output);
      extern void quadtimer_init(IMXRT_TMR_t *p);
      extern void quadtimerWrite(IMXRT_TMR_t *p, unsigned int submodule, uint16_t val);
      extern void quadtimerFrequency(IMXRT_TMR_t *p, unsigned int submodule, float frequency);
    }
    
    
    void connectTimer(uint8_t adc_num) {
    
      CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON); //turn clock on for xbara1
      // setup connections for ADC0 or ADC1
      if(adc_num){
        XBAR_IN = XBARA1_IN_QTIMER4_TIMER3; //QTIMER4_0 for ADC1
        XBAR_OUT = XBARA1_OUT_ADC_ETC_TRIG10;  
      }
      else{
        XBAR_IN = XBARA1_IN_QTIMER4_TIMER0;  //QTIMER4_3 for ADC0
        XBAR_OUT = XBARA1_OUT_ADC_ETC_TRIG00;   
      }
      xbar_connect(XBAR_IN, XBAR_OUT);
    }
    
    
    void startTimer(uint8_t timer, float freq)
    {
      
      Serial.printf("Timer: %d, Freq: %f\n", timer, freq);
      if (timer == 1) {
        Serial.println("Try to init QTimer4_0"); Serial.flush();
        //quadtimer_init(&IMXRT_TMR4);
        quadtimerFrequency(&IMXRT_TMR4, 0, freq);
        quadtimerWrite(&IMXRT_TMR4, 0, 5);
        Serial.println("After Qtimer4_0 init"); Serial.flush();
       } 
      if (timer == 2){
        Serial.println("Try to init QTimer4_3"); Serial.flush();
        //quadtimer_init(&IMXRT_TMR4);
        quadtimerFrequency(&IMXRT_TMR4, 3,  freq);
        quadtimerWrite(&IMXRT_TMR4, 3, 5);
        Serial.println("After Qtimer4_3 init"); Serial.flush();  
        }  
    }
    
    
    #endif
    
    void adcetc0_isr() { 
    ADC_ETC_DONE0_1_IRQ |= 1 << trigger_adc_0;   // clear 
    
    for (uint8_t i = 0; i < channels; i++) {
     rb_adc_0[i].push_back(ADC_ETC_GetADCConversionValue( trigger_adc_0, i ));  
    }    
    timest.push_back(micros());
    //timest.push_back(ARM_DWT_CYCCNT);
      asm("dsb");  
    }
    
    void adcetc1_isr() {   
      ADC_ETC_DONE0_1_IRQ |= (1 << (trigger_adc_1+16));   // clear 
      
     for (uint8_t k = 0; k < channels; k++) { 
       rb_adc_1[k].push_back(ADC_ETC_GetADCConversionValue( trigger_adc_1, k ));  
     }
        asm("dsb");
     }
    
    void adc_etc_reset() {
      //  first do soft reset and/or let complete
      IMXRT_ADC_ETC.CTRL = ADC_ETC_CTRL_SOFTRST; // SOFTRST
      IMXRT_ADC_ETC.CTRL &= ~ADC_ETC_CTRL_SOFTRST; // SOFTRST
      delay(5);
    }
    
    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;
    
      delay(5);
    
      // Now lets process ADC1(ADC_0) pin...
    
      for (uint8_t i = 0; i < numPins_adc_0; i++) {    
        adc_pin_channel = ADC::channel2sc1aADC0[adc_0_pinArray[i]];
        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);
    
    }
    
    void adc1_etc_ctrl_init(uint8_t chainLength_1, int adc_1_pinArray[],
                            uint8_t trigger_adc_1, bool DMA)
    {
      int numPins_adc_1 = sizeof(adc_1_pinArray);
      uint8_t adc_pin_channel;
    
      delay(5);
    
      // Now lets do ADC2(ADC_1)
      if (chainLength_1 >= 0) {
        for (uint8_t i = 0; i < numPins_adc_1; i++) {
          adc_pin_channel = ADC::channel2sc1aADC1[adc_1_pinArray[i]];
          if (adc_pin_channel == 0xff)
            Serial.printf("ADC_ETC_INIT: pin:%d did not 2map to channnel on %d\n", adc_1_pinArray[i], ADC_1);
        }
        // ADC2 (ADC_1)
        IMXRT_ADC_ETC.CTRL |= ADC_ETC_CTRL_TRIG_ENABLE(1 << trigger_adc_1); // Add in our trigger
        Serial.printf("ADC_ETC_CTRL: %x\n",  IMXRT_ADC_ETC.CTRL);
    
        IMXRT_ADC_ETC.TRIG[trigger_adc_1].CTRL = ADC_ETC_TRIG_CTRL_TRIG_CHAIN(chainLength_1 - 1);
    
        if (DMA)
          IMXRT_ADC_ETC.DMA_CTRL |= ADC_ETC_DMA_CTRL_TRIQ_ENABLE(trigger_adc_1);
      }
    
      Serial.printf("ADC2: HC0:%x HS:%x CFG:%x GC:%x GS:%x\n", ADC2_HC0, ADC2_HS,  ADC2_CFG, ADC2_GC, ADC2_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);
    
    }
    
    
    void ADC_ETC_SetTriggerChainConfig(uint8_t adc_num, 
                                       uint8_t done,
                                       uint32_t triggerGroup,
                                       uint32_t chainGroup, //0, 1, 2, 3 (0_1, 3_2....
                                       uint8_t pin1, uint8_t pin2)
    {
       uint8_t adc_pin_channel;
    
        switch (chainGroup)
        {
          case 0U: /* Configurate trigger chain0 and chain 1. */
            if(adc_num == 1) {
              adc_pin_channel = ADC::channel2sc1aADC1[pin1];
            } 
            else {
              adc_pin_channel = ADC::channel2sc1aADC0[pin1];
            }
            IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_1_0 |= ADC_ETC_TRIG_CHAIN_B2B0 |
            ADC_ETC_TRIG_CHAIN_IE0(0) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
              | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;
    
            if(pin2 > -1)
            {
              if(adc_num == 1) {
                adc_pin_channel = ADC::channel2sc1aADC1[pin2];
              } 
              else {
                adc_pin_channel = ADC::channel2sc1aADC0[pin2];
              }
              IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_1_0 |= ADC_ETC_TRIG_CHAIN_B2B1 | 
                ADC_ETC_TRIG_CHAIN_IE1(0) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
                | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(adc_pin_channel); 
            }
            break;
          case 1U: /* Configurate trigger chain2 and chain 3. */
            if(adc_num == 1) {
              adc_pin_channel = ADC::channel2sc1aADC1[pin1];
            } 
            else {
              adc_pin_channel = ADC::channel2sc1aADC0[pin1];
            }
            //Serial.print("ADC Pin3 Channel: "); Serial.println(adc_pin_channel);
            IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_3_2 = 
              ADC_ETC_TRIG_CHAIN_B2B0 |
              ADC_ETC_TRIG_CHAIN_IE0(0) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
              | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;
            if(pin2 > -1)
            {
              if(adc_num == 1) {
                adc_pin_channel = ADC::channel2sc1aADC1[pin2];
              } 
              else {
                adc_pin_channel = ADC::channel2sc1aADC0[pin2];
              }
            //Serial.print("ADC Pin4 Channel: "); Serial.println(adc_pin_channel);
              IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_3_2 |= ADC_ETC_TRIG_CHAIN_B2B1 | 
                ADC_ETC_TRIG_CHAIN_IE1(0) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
                | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(adc_pin_channel); 
                //Serial.print("TRIG[0].CHAIN_3_2: "); Serial.println(IMXRT_ADC_ETC.TRIG[0].CHAIN_3_2, HEX);
            }
            break;
          case 2U: /* Configurate trigger chain4 and chain 5. */
            if(adc_num == 1) {
              adc_pin_channel = ADC::channel2sc1aADC1[pin1];
            } 
            else {
              adc_pin_channel = ADC::channel2sc1aADC0[pin1];
            }
            IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_5_4 = 
              ADC_ETC_TRIG_CHAIN_B2B0 |
              ADC_ETC_TRIG_CHAIN_IE0(0) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
              | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;
            if(pin2 > -1)
              {
              if(adc_num == 1) {
                adc_pin_channel = ADC::channel2sc1aADC1[pin2];
              } 
              else {
                adc_pin_channel = ADC::channel2sc1aADC0[pin2];
              }
              IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_5_4 |= ADC_ETC_TRIG_CHAIN_B2B1 | 
                ADC_ETC_TRIG_CHAIN_IE1(done) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
                | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(adc_pin_channel); 
            }
             break;
          case 3U: /* Configurate trigger chain6 and chain 7. */
            if(adc_num == 1) {
              adc_pin_channel = ADC::channel2sc1aADC1[pin1];
            } 
            else {
              adc_pin_channel = ADC::channel2sc1aADC0[pin1];
            }
            IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_7_6 = 
              ADC_ETC_TRIG_CHAIN_B2B0 |
              ADC_ETC_TRIG_CHAIN_IE0(done) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
              | ADC_ETC_TRIG_CHAIN_HWTS0(1) | ADC_ETC_TRIG_CHAIN_CSEL0(adc_pin_channel) ;
            if(pin2 > -1)
            {
              if(adc_num == 1) {
                adc_pin_channel = ADC::channel2sc1aADC1[pin2];
              } 
              else {
                adc_pin_channel = ADC::channel2sc1aADC0[pin2];
              }
              IMXRT_ADC_ETC.TRIG[triggerGroup].CHAIN_7_6 |= ADC_ETC_TRIG_CHAIN_B2B1 | 
                ADC_ETC_TRIG_CHAIN_IE1(done) /*| ADC_ETC_TRIG_CHAIN_B2B0 */
                | ADC_ETC_TRIG_CHAIN_HWTS1(1) | ADC_ETC_TRIG_CHAIN_CSEL1(adc_pin_channel); 
            }
             break;
          default:
             break;
        }
    }
    
    /*!
      Taken from SDK
      brief Get ADC conversion result from external XBAR sources.
      For example, if triggerGroup is set to 0U and chainGroup is set to 1U, which means the API would
      return Trigger0 source's chain1 conversion result.
    
      param base ADC_ETC peripheral base address.
      param triggerGroup Trigger group index. Available number is 0~7.
      param chainGroup Trigger chain group index. Available number is 0~7.
      return ADC conversion result value.
      Zo te zien kan dit nog best beter, ik denk dat de trigger chains te kort zijn (meer dan 2 pins).
      
      Het resultaat is iig 12 bits en NIET 32, dus een 16bits output voor het resultout zou veel corrector zijn!
    */
    uint16_t ADC_ETC_GetADCConversionValue(uint32_t triggerGroup, uint32_t chainGroup)
    {
      //assert(triggerGroup < ADC_ETC_TRIGn_RESULT_1_0_COUNT);
    
      uint32_t mADCResult;
      uint8_t mRemainder = chainGroup % 2U;
    
      switch (chainGroup / 2U)
      {
        case 0U:
          if (0U == mRemainder)
          {
            mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_1_0);
          }
          else
          {
            mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_1_0) >> (16U);
          }
          break;
        case 1U:
          if (0U == mRemainder)
          {
            mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_3_2);
          }
          else
          {
            mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_3_2) >> (16U);
          }
          break;
        case 2U:
          if (0U == mRemainder)
          {
            mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_5_4);
          }
          else
          {
            mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_5_4) >> (16U);
          }
          break;
        case 3U:
          if (0U == mRemainder)
          {
            mADCResult = (0xFFFU) & (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_7_6);
          }
          else
          {
            mADCResult = (IMXRT_ADC_ETC.TRIG[triggerGroup].RESULT_7_6) >> (16U);
          }
          break;
        default:
          return 0U;
      }
      return mADCResult;
    }

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,147
    Indeed in normal use micros() can be called one or more times per us : i.e. 1-3 M/sec but will only return 1M unique values/sec.

    Not indicated how this was determined? :: "works up to about 2.3kHz, at higher frequencies micro(s) returns wrong values >50% of the times"

    micros() works by taking the current millis() and the ARM_CYCCNT offset from when that last millis_tick was updated to the current ARM_CYCCNT.

    So micros() only depends on those two stored values and the current ARM_CYCCNT when called. It takes about 37 cycles to complete the var reference and math to return the micros() value. It is interrupt aware in case the interrupt is updating the millis_tick, but any interrupt will cause a re-read of a few cycles. It does not disable interrupts and depends on no timers - just the ARM_CYCCNT.

    In the code is this ref:
    Code:
    void adcetc0_isr() { 
    ADC_ETC_DONE0_1_IRQ |= 1 << trigger_adc_0;   // clear 
    
    for (uint8_t i = 0; i < channels; i++) {
     rb_adc_0[i].push_back(ADC_ETC_GetADCConversionValue( trigger_adc_0, i ));  
    }    
    timest.push_back(micros());
    //timest.push_back(ARM_DWT_CYCCNT);
      asm("dsb");  
    }
    Reading ARM_CYCCNT alone is faster - QUESTION: Was that compared to return reasonable numbers in the same code?
    > those numbers will lack 'context' ref to millis() but showing : CYCCNT_Value(last) - CYCCNT_Value(previous) would give the count of cycles between

    That same math should work on the stored micros_Value() from the Circular_Buffer

    Only oddity is that: "const uint8_t channels = 6;" will have 6 ADC data reads stored for each "timest.push_back(micros());"
    Where both use the same size of buflen_0:
    Code:
    Circular_Buffer<uint16_t,buflen_0> rb_adc_0[6];
    Circular_Buffer<int32_t,buflen_0> timest;
    So there will be 6X more valid data values stored than timestamps.

    So this will not be printing APPLES to APPLES:
    Code:
        for (uint8_t i=0; i < 64;i++){
          Serial.printf("%d %d\n",printstamps[i]),buffer0_1[i];
    Perhaps something like this would show expected results?:
    Code:
        for (uint8_t i=0; i < 64;i++){
          Serial.printf("%d %d\n",printstamps[i/6]),buffer0_1[i];

  3. #3
    Junior Member
    Join Date
    Jun 2021
    Posts
    6
    I determined that it did not work by simply looking at the output of the serial port:
    This is what the output looks like with QTimer4_0 running at 2kHz:
    Code:
    12990026 1202
    12990511 1202
    12990995 1202
    12991479 1202
    12991964 1202
    12992448 1202
    12992932 1202
    12993417 1202
    12993901 1202
    12994386 1202
    12994870 1202
    12995354 1202
    12995839 1202
    12996323 1202
    12996808 1202
    And this is the output when QTimer4_0 is running at 2.4kHz:
    Code:
    687 1202
    1 1202
    1 1202
    3089 1202
    536915528 1202
    0 1202
    2 1202
    1 1202
    1 1202
    3089 1202
    536915528 1202
    536915968 1202
    536915942 1202
    536915941 1202
    10101 1202
    I have already replaced micros() with ARM_DWT_CYCCNT, which has the same strange behavior.
    The reason there is only 1 timestamp value is because I only wanted to check if the function adcetc0_isr() was being triggered at the samplerate set. The time difference between each conversion of the trigger chain depends on the ADC configuration
    in this case it's about 0.85us I believe. Switching between adc pins is done with the ADC_ETC so I simply wait for all conversion to be finished and then an interrupt calls for adcetc0_isr and can buffer all adc conversions at once. So in case of timestamping, all values are buffered more or less at the same time.

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,147
    Quote Originally Posted by Wern_88 View Post
    I determined that it did not work by simply looking at the output of the serial port:
    This is what the output looks like with QTimer4_0 running at 2kHz:
    ...

    I have already replaced micros() with ARM_DWT_CYCCNT, which has the same strange behavior.
    ...
    Thanks ... That answers my wonderment ... or suspicion ... that was tried to some end and the result wasn't right.

    If it was failing the same with :: ARM_DWT_CYCCNT

    Then the problem is not micros() - but rather something else in the code - perhaps :: Circular_Buffer buffer getting out of order as referenced or something.

    Until ARM_DWT_CYCCNT wraps, the uint32_t always increases on each clock tick and have never seen it not return an expected value - even as I wrote and tested the micros() some many millions of times fix to use ARM_DWT_CYCCNT instead of a slower running clock with poor resolution.

    Just seeing that indeed there are [6] of the rb_adc_0 - so maybe the 6::1 storage should be right - but if ARM_DWT_CYCCNT isn't saved and recalled as expected then the storage isn't working.

  5. #5
    Junior Member
    Join Date
    Jun 2021
    Posts
    6
    I changed the code and removed all the Circular_Buffer parts and now only timestamped at each adcetc0_isr intterupt, the problem still persisted, therefore I tried slightly overclocking to see if that could work. And to my surprise it really did, I was able to increase the frequency to about 2.8kHz by overclocking the Teensy to 750MHz, but naturally this is not the way forward. So I wonder if where to go from here with [B]micros()/ARM_DWT_CYCCNT[/B}, for one reason they) can not give reliable results with my code above a certain samplerate. Likely this has something to do with using interrupts instead of dma to store the results of the ADC_ETC.

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,147
    That is odd. Perhaps from ADC usage, but if handled right those numbers should be returning right.

    One of the micros() test for its beta release IIRC was a 1MHz timer interrupt watching micros and probably CYCCNT.

    I have an ADC reading 2 pins at 238K/sec on a T4. Perhaps can add some micros/CYCCNT storage in that loop and on an 3+KHz interrupt and verify they are reading right.

  7. #7
    Junior Member
    Join Date
    Jun 2021
    Posts
    6
    I have been looking through your post history and github, but can't seem to find the relevant ADC code you describe. Can you please link it to me?
    Could changing the code to use DMA requist instead of IRQ's decrease the CPU load significantly?

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,147
    Quote Originally Posted by Wern_88 View Post
    I have been looking through your post history and github, but can't seem to find the relevant ADC code you describe. Can you please link it to me?
    Could changing the code to use DMA requist instead of IRQ's decrease the CPU load significantly?
    Assuming this was for @defragster? Current code not posted - just a modification of a drum piezo example ( twin ADC reads of 5 channels each ) done some months back and revisited recently in another thread ...

    That is ref'd here :: pjrc.com/threads/67301-Satus-of-Teensy-4-0-ADC

    Following the link there goes to the sample code written to get all 10 channels read some 133K times per second. Not sure what happened on the 'user' end - but worked that up here to see using that code. It runs in loop() - but the read could probably moved to a timer driven ISR() to read the data when loop() might be bogged down.

    Current unposted code is just a two channel version of that to put a sine wave from two T_3.6 DACs to the IDE Plotter. Have not gone back to that for time stamp test ...

  9. #9
    Junior Member
    Join Date
    Jun 2021
    Posts
    6
    Quote Originally Posted by defragster View Post
    Assuming this was for @defragster? Current code not posted - just a modification of a drum piezo example ( twin ADC reads of 5 channels each ) done some months back and revisited recently in another thread ...

    That is ref'd here :: pjrc.com/threads/67301-Satus-of-Teensy-4-0-ADC

    Following the link there goes to the sample code written to get all 10 channels read some 133K times per second. Not sure what happened on the 'user' end - but worked that up here to see using that code. It runs in loop() - but the read could probably moved to a timer driven ISR() to read the data when loop() might be bogged down.

    Current unposted code is just a two channel version of that to put a sine wave from two T_3.6 DACs to the IDE Plotter. Have not gone back to that for time stamp test ...
    Yes I forgot to mention you (@defragster) correctly. Thanks for pointing me in the right direction, hope that it helps me improve my performance. Will post the results!

  10. #10
    Junior Member
    Join Date
    Jun 2021
    Posts
    6

    Update, it works!

    @Defragster, the 10 channel code was insightfull but was not suitable for my needs, since I need two sets of 6 channels with different samplerates. But I managed to find the cause of the problem, the original code from @msj513 used the quadtimerFrequency from pwm.c and if I am correct they need to oversample the frequency that I require to get different pulsewidth at a certain frequency with a 16bits resolution. My found limit of about 2.3kHz matches the F_BUS_ACTUAL at 150MHz since 2^16 * 2300 is a little over 150E6. I created a simplified startTimer() using the register settings from input_adc.cpp from the audio library and now it works perfectly with timestamps, I have no idea what the samplerate limit is but it seems to be sufficiŽnt for my needs. Will post the code when I am done cleaning it up.

  11. #11
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,147
    Very good you got to the problem and worked through it. Will be interesting to have the solution posted.

    Like that 10 channel code that was special purpose for the example at hand - probably the same for the code from @mjs513.

Posting Permissions

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