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

Thread: Solved: ADCs on Teensy 4.0 are way too fast?!

  1. #1
    Junior Member
    Join Date
    Jan 2020
    Posts
    17

    Solved: ADCs on Teensy 4.0 are way too fast?!

    Hello together!

    I have a question regarding the maximum ADC Speed of the teensy 4.0. According to the datasheet of the IMXRT1060 the minimal total conversion time is ~750nS,
    but with the code below I'm way too fast for that and getting conversion times of about ~200nS!!! (For 12Bit!!!)

    The highest possible clock for the ADCs should be 40Mhz, so the values I'm measuring can't be accurate!
    Does anyone know where these strange measurements come from?
    Have I set any of the registers wrong, or is the measurement itself done wrong?

    Thank you in advance for any help

    Measurements:

    Click image for larger version. 

Name:	adcspeedquestion.PNG 
Views:	41 
Size:	12.5 KB 
ID:	18921
    (for getting Nanoseconds: 10^9/ticks)

    Code:

    Code:
    //Verwendetes Datenblatt: IMXRT1060 Reference Manual
    #include <DMAChannel.h>
    #define PrintRegister(x) Serial.print(#x" 0x"); Serial.println(x, BIN); //bequeme Schreib-Definition für seriellen Monitor (zur Ausgabe von Registerwerten)
    #define BufferSize 1 //Größe aller DMA-Buffer
    /*  ADC Muxing Options (für ADC1-Inputs (auch mit ADC2 messbar!) S. 289):
        ADC1_IN7 GPIO_AD_B1_02 (A0) // ADC1_IN8 GPIO_AD_B1_03 (A1) // ADC1_IN12 GPIO_AD_B1_07 (A2)
        ADC1_IN11 GPIO_AD_B1_06 (A3) // ADC1_IN6 GPIO_AD_B1_01 (A4) // ADC1_IN5 GPIO_AD_B1_00 (A5)
        ADC1_IN15 GPIO_AD_B1_10 (A6) // ADC1_IN0 GPIO_AD_B1_11 (A7) // ADC1_IN13 GPIO_AD_B1_08 (A8)
        ADC1_IN14 GPIO_AD_B1_09 (A9) // ADC1_IN1 GPIO_AD_B0_12 (A10) // ADC1_IN2 GPIO_AD_B0_13 (A11)  */
    #define pinMessungA 7 //A0 --> langsamer => Strom!
    #define pinMessungB 14 //A9 --> schneller => Spannung! (wird als 2. initialisiert => Priorität pinMessungB > Priorität pinMessungA
    
    //ADC General Status-Register S. 3494
    bool ADACT1;  bool ADACT2; //Conversion Active Flag
    bool CALF1;   bool CALF2;  //Calibration Failed Flag
    bool AWKST1;  bool AWKST2; //Asynchronous wakeup interrupt status (0 = no asynchronous interrupt)
    
    DMAMEM static uint16_t SpannungBufferDMA[BufferSize]; //Buffer für Spannung im DMA-Speicher
    DMAChannel dma1(false);
    DMAMEM static uint16_t StromBufferDMA[BufferSize]; //Buffer für Strom im DMA-Speicher
    DMAChannel dma2(false);
    volatile uint32_t ticks = 0;
    volatile uint32_t ticks2 = 0;
    
    void setup() {
      Serial.begin(115200); //Baudrate einstellen für serielle Verbindung
      while (!Serial);
      
      setupADCs(pinMessungA, pinMessungB);
      if (checkADCs()) { //Laufen beide ADCs fehlerfrei?
        Serial.println("\nBeide ADCs erfolgreich initialisiert!");
      } else {
        Serial.println("\nBeide ADCs laufen nicht! (INIT FEHLER)\n");
      }
      delay(1000);
    }
    
    void loop() {
      printReadings(); //Speed in nS = (10^9/ticks)
      arm_dcache_delete(SpannungBufferDMA, BufferSize + 1); //Cache leeren + 1?
      arm_dcache_delete(StromBufferDMA, BufferSize + 1); //Cache leeren
      delay(1000);
    }
    
    bool checkADCs() {
      /*                         AWKST | CALF | ADACT
        ADCx_GS = 3Bit...          1       0      1
        Maske mit & für ADACT:     0       0      1  */
      ADACT1 = ADC1_GS & 001;   ADACT2 = ADC2_GS & 001;
      CALF1 =  ADC1_GS & 010;   CALF2 =  ADC2_GS & 010;
      AWKST1 =  ADC1_GS & 100;  AWKST2 =  ADC2_GS & 100;
      if (ADACT1 && ADACT2 && !CALF1 && !CALF2 && !AWKST1 && !AWKST2) return 1;
      return 0;
    }
    
    void setupADCs(int pin1, int pin2) {
      //Datasheet Seite 3449 (i.MX RT1064)
      cli(); //Interrupts deaktivieren
      Serial.println("Setup ADCs...\n");
      //ADC-1:
      ADC1_GC = 0x42; //1000010
      ADC1_CFG = 0x408; //10000001000
      ADC1_HC0 = pin1; //Kanal 7 = A0
      PrintRegister(ADC1_GC);
      PrintRegister(ADC1_CFG);
      PrintRegister(ADC1_HC0);
      //DMA für ADC1 initialisieren:
      dma1.begin(true); //Speicher für DMA-Kanal allozieren
      dma1.source((uint16_t &) ADC1_R0); //ADC1 als Triggerquelle wählen
      dma1.destinationBuffer(SpannungBufferDMA, BufferSize + 1); //Ziel wählen
      dma1.interruptAtCompletion();
      dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1); //DMA durch ADC1 triggern lassen!
      //ADC-2:
      ADC2_GC = 0x42; //11000010
      ADC2_CFG = 0x408; //10000001000
      ADC2_HC0 = pin2; //Kanal 14 = A9
      PrintRegister(ADC2_GC);
      PrintRegister(ADC2_CFG);
      PrintRegister(ADC2_HC0);
      //DMA für ADC2 initialisieren:
      dma2.begin(true); //Speicher für DMA-Kanal allozieren
      dma2.source((uint16_t &) ADC2_R0); //ADC2 als Triggerquelle wählen
      dma2.destinationBuffer(StromBufferDMA, BufferSize + 1);
      dma2.interruptAtCompletion();
      dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC2);
      //Direct Memory Access Interrupts aktivieren:
      dma1.attachInterrupt(dma1_isr);
      dma1.enable();
      dma2.attachInterrupt(dma2_isr);
      dma2.enable();
      sei(); //Interrupts wieder aktivieren
    }
    
    void dma1_isr(void) { //ADC
      dma1.clearInterrupt();
      ticks++; //Sample-Counter
      asm volatile ("dsb"); //Inline Assembler Anweisung
      /* https://developer.arm.com/docs/dui0646/a/the-cortex-m7-instruction-set/instruction-set-summary
         DSB acts as a special data synchronization memory barrier.
         Instructions that come after the DSB, in program order, do not execute until the DSB instruction completes.
         The DSB instruction completes when all explicit memory accesses before it complete. */
    }
    
    void dma2_isr(void) { //ADC
      dma2.clearInterrupt();
      ticks2++; //Sample-Counter
      asm volatile ("dsb");
    }
    
    void printReadings() { //Debugging
      static int prev;
      static int prev2;
      Serial.printf("%d ticks A0 = %d \n", ticks - prev, SpannungBufferDMA[0]);
      prev = ticks;
      Serial.printf("%d ticks A9 = %d \n", ticks2 - prev2, StromBufferDMA[0]);
      prev2 = ticks2;
    }
    Last edited by KurtE; 01-31-2020 at 11:37 PM.

  2. #2
    Junior Member
    Join Date
    Jan 2020
    Posts
    17
    [CASE SOLVED]

    I simply overclocked the ADC with the ipg_clock (150Mhz)
    => Divider (ADIV = 2) gives me 37,5Mhz @ which I'm getting exacly the result the datasheet claims!

    BUT: It can be overclocked!!! Currently at 75Mhz and ADSTS(1) for getting Results @ constant 400nS!

    Q: How can I edit the Thread's title to "solved" ???

  3. #3
    Hello Marty Brown. I am in need of doing AD conversions of around 100 ns and then store the number in RAM within ~50 ns after that. Your code does not look much like Arduino code to my untrained eye and having the comments in German makes it much harder. Any possibility you could translate the comments to English. Thanks!

  4. #4
    Junior Member
    Join Date
    Jan 2020
    Posts
    17
    Yes of course!

    I'm trying to post a version with english comments the next few days!

Posting Permissions

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