Comparing Crystals using T0 and T1


If you need to read higher frequencies, Int0 and Int1 turn out to be too slow.
In order to compare two frequencies you actually need three counters:
  • for source 1
  • for source 2
  • for the gate time.
  • All the Arduinos have three counters but the only hardware counter inputs are T0 (Arduino-Pin d4) and T1 (Arduino-Pin d5). In fact, T1 is a 16-bit counter, but as both counters are to be handled equally, is used only as an 8-bit counter.
    T0 refers to Timer-0 which is used for the millis() and delay() fucntions but we don't use these functiones here.
    The remaining timer is Timer-2 which we will use as the gate timer.

    Frequencies up to 4 MHz can be registered at d4 and d5. The results can be displayed in the Serial plotter.

    /* Read two frequencies
      3rd July 2018
      As ISR (TIMER0_OVF_vect) is already used by Arduino
      we go for ISR (TIMER0_COMPA_vect), the same for Timer-1.
      It will trigger interrupts at compare match.
      Input: Pin D4 for Timer-0 (T0)
      Input: Pin D5 for Timer-1 (T1) also used as 8-bit-Timer
      Timer-2 now controls the gate time
      Jumper pin-8 - pin-9:
      jumper off: difference only
      jumper on: both frequencies
      on change Serial Plotter gets cleared
    */
    
    long gateTime = 1000;
    const word ocr0a = 250;
    const word ocr1a = ocr0a;
    byte tcnt0;
    word tcnt1;
    byte oldTCCR0B;
    const byte modeOut = 8;
    const byte modeIn = 9;
    boolean mode, oldMode;
    
    // ussed in ISR and main program
    volatile boolean counter2Ready;
    volatile word cmpMatch0Count;
    volatile word cmpMatch1Count;
    volatile word timer2Ticks;
    volatile word timer2Period;
    
    void setup () {
      Serial.begin(115200);
      Serial.println(__FILE__);
      Serial.println("connect pin-8 & pin-9 for diff");
      pinMode(modeOut, OUTPUT);
      pinMode(modeIn, INPUT_PULLUP);
      oldMode = getMode();
    }
    
    boolean getMode() {
      return digitalRead(modeIn);
    }
    
    void loop () {
      startCounting (gateTime);
      while (!counter2Ready);
      // calculate total counts
      long timer0Counts = tcnt0 + (long) cmpMatch0Count * (ocr0a + 1);
      long timer1Counts = tcnt1 + (long) cmpMatch1Count * (ocr1a + 1);
      // adjust counts by counting interval to give frequency in Hz
      float frq0 = timer0Counts * 1000.0 / timer2Period;
      float frq1 = timer1Counts * 1000.0 / timer2Period;
      mode = getMode();
      if (mode) {
        frq1 = frq1 - frq0;
        frq0 = frq1;
      }
      int n = 1;
      if (mode != oldMode) n = 498;
      for (int i = 0; i < n; i++) {
        Serial.print("Frequency:\t");
        Serial.print(frq0);
        Serial.print("\tHz\t");
        Serial.print(frq1);
        Serial.print("\tHz\t");
        Serial.println();
      }
      // let serial stuff finish
      delay(100);
      oldMode = mode;
    }
    
    void startCounting (word ms) {
      oldTCCR0B = TCCR0B;
      cli();
      // ============== the gate: Timer-2: ==================
      counter2Ready = false;         // time not up yet
      timer2Period  = ms;            // how many 1 ms counts to do
      timer2Ticks   = 0;             // reset interrupt counter
      // Timer 2 - gives us our 1 ms counting interval
      // 16 MHz clock (62.5 ns per tick) - prescaled by 128
      //  counter increments every 8 Ás.
      // So we count 125 of them, giving exactly 1000 Ás (1 ms)
      TCCR2A = 2;   // CTC mode
      OCR2A  = 124; // count up to 125  (zero relative!!!!)
      // Timer 2 - interrupt on match (ie. every 1 ms)
      TIMSK2 = 2;   // enable Timer2 Interrupt
      // Reset prescalers
      GTCCR = 2;    // reset prescaler now
      // start Timer 2
      TCCR2B = 5 ;  // prescaler of 128
      // set counter-2 to zero
      TCNT2 = 0;
      // ================= the gate ==================
    
      // Timer 0 - counts events on pin D4
      TCCR0A = 2; // CTC mode
      // 1 1 1 External clock source on T0 pin (D4). Clock on rising edge.
      TCCR0B = 7;
      OCR0A  = ocr0a; // count up to ocr0a
      // set counter-0 to zero:
      TCNT0 = 0;
      cmpMatch0Count = 0;             // no cmpMatchs yet
      // start Timer 0
      TIFR0  = 2; // Timer/Counter0 Interrupt Flag Register
      // OCIE0A: Timer/Counter0 Output Compare Match A Interrupt Enable
      TIMSK0 = 2;
    
      // Timer 1 - counts events on pin D5
      TCCR1A = 0;
      // 1 1 1 External clock source on T1 pin (D5). Clock on rising edge.
      TCCR1B = 8 + 7;  // CTC  mode + External clock source on T1 pin
      OCR1A  = ocr1a;  // count up to ocr1a
      // set counter-1 to zero:
      TCNT1 = 0;
      cmpMatch1Count = 0;
      // start Timer 1
      TIFR1  = 2; // Timer/Counter1 Interrupt Flag Register
      // OCIE1B: Timer/Counter1, Output Compare B Match Interrupt Enable
      TIMSK1 = 2;
      // enable interrupts
      sei();
    }
    
    ISR (TIMER0_COMPA_vect) {
      ++cmpMatch0Count;       // count number of Counter0 compare matches
    }
    
    ISR (TIMER1_COMPA_vect) {
      ++cmpMatch1Count;       // count number of Counter1 compare matches
    }
    
    //****** gate time *******************************************
    /*  Timer2 Interrupt Service is invoked by hardware Timer-2
         every 1 ms = 1000 Hz. 16Mhz / 128 / 125 = 1000 Hz    */
    ISR (TIMER2_COMPA_vect) {
      if (++timer2Ticks < timer2Period) return;
      // end of gate time, measurement ready
      tcnt0 = TCNT0;
      tcnt1 = TCNT1;
      // restart timer 0
      TCCR0B = oldTCCR0B;
      counter2Ready = true;
    }
    






    contact: nji(at)gmx.de