Hardwarecounter für Frequenzmessung

Hier ist ein einfaches Frequenzmessgerät für Arduino bis 33kHz.
Da nicht jeder einen Frequenzgenerator hat, ist dieser zu Testen bereits eingebaut :slight_smile:
Dann muss Pin 3 und Pin 4 verbunden werden.

// ______________________________________________________________________________________________
// ______________________________________________________________________________________________
//
// Frequenzgenerator   +   Frequenzmessgerät bis 33 kHz
//
// (C) Helmut Weber 2/2015
//
//
// Für Fortgeschrittene:
//
// An Pin 3 wird die Zeit  / Frequenz eines externen Rechtecksignals gemessen
// Da nicht jeder über einen Frequenzgenerator verfügt, wird in einer vom Timer-1 angesteuerten
// Interrupt Routine eine Rechteckfrequenz an Pin 4 erzeugt.
// Es müssen dann Pin 4 (Sender) und Pin 3 (Empfänger) verbunden werden.
//
// Natürlich können an Pin 3 auch externe Frequenzquellen angschlossen werden
//
// Ergebnis: Periodendauer / Frequenzmesser 20 Hz - 33 kHz
// ______________________________________________________________________________________________
// ______________________________________________________________________________________________


// Anzahl der Messungen für Genauigkeit
// Für Frequenzen unter 1000 Hz entsprechend herabsetzen:
#define SAMPLES 1000


//                             Makros zum Setzen, Löschen / Testen von Bits
// ______________________________________________________________________________________________
// set bit
static inline void BIT_SET(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline));
static inline void BIT_SET(volatile uint8_t *target, uint8_t bit){
	*target |= (1<<bit);
};
 
// clear bit
static inline void BIT_CLEAR(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline));
static inline void BIT_CLEAR(volatile uint8_t *target, uint8_t bit){
	*target &= ~(1<<bit);
};

// test bit
static inline bool BIT_TEST(volatile uint8_t *target, uint8_t bit) __attribute__((always_inline));
static inline bool BIT_TEST(volatile uint8_t *target, uint8_t bit){
	return(*target & (1<<bit));
};


// ______________________________________________________________________________________________
// Globale Variable

bool lastPin3=true;               // Grundzustand: Taste an Pin 3 NICHT gedrückt
unsigned long lastChange3Time=micros();
unsigned long Period=micros();

// ______________________________________________________________________________________________
//Timer Interrupt Routine
// Wird entsprechend der eingestellten Zeit in SETUP
// zyklisch aufgerufen und toggelt Ausgabe an Pin 4
// Es ist gewissermaßen ein eigenes Programm.

ISR(TIMER1_COMPA_vect,ISR_NAKED)
{
  //digitalWrite(4, !digitalRead(4));
  if (BIT_TEST(&PORTD,4)) BIT_CLEAR(&PORTD,4);
  else                    BIT_SET  (&PORTD,4);
 asm(" reti");
}


// ______________________________________________________________________________________________
// Interrupt Routine:
// Wird bei jedem Wechsel an Pin 3 aufgerufen, egal was das Hauptprogramm (LOOP) gerade
// macht. Es ist sozusagen ein eigenes Programm.
unsigned long m;
unsigned int lp;
unsigned long per;

ISR(  PCINT2_vect) {            // wird bei jeder Änderung am Pin 3 aufgerufen
  //SAVE_CONTEXT;               // bei kleinen Programmen organisiert der Compiler das
  BIT_SET(&PORTB,1);
  m=micros();                   // Aktuelle Zeit in Mikrosekunden
  per+=(m - lastChange3Time);
  lp++;
  if (lp==SAMPLES) {
    Period=per;                 // SAMPLES-fache Periodendauer
    lp=0; per=0;
  }
  
  lastChange3Time=m;            // Sichern von dieser Zeit
  BIT_CLEAR(&PORTB,1);
  //RESTORE_CONTEXT;
  //asm("reti");
} 


// ______________________________________________________________________________________________
// Aufruf einmalig am Programm-Start:

void setup() {
  Serial.begin(115200);
  pinMode(4,OUTPUT);            // Für Frequenzgenerator
  pinMode(9,OUTPUT);
  pinMode(3,INPUT_PULLUP);
  
// ============================================
// Timer 1 einrichten = Frequenz-Generator:
// Setup Timer1 IRQ     für Pulse an Pin 4
// ============================================
  cli();
  
  TIMSK1 = (1 << OCIE1A);
  TCCR1A = 0;
  TCCR1B = (1 << WGM12 ) ;                           // Configure timer 1 for CTC mode
  TCCR1B |= (( 1 << CS11  ));                        // Prescaler=8 => 500ns
  
  // 20 Hz
  //OCR1A = 49999;                                   // 20 Hz
  
  // 1000 Hz
  //OCR1A =   999;                                   //  1000 Hz
  
  // 5000 Hz
  //OCR1A =   199;                                   //  5000 Hz
  
  // 10000 Hz
  //OCR1A =  99;                                     //  10000 Hz (alle 50us 1 Interrupt!)  
  
  // 20000 Hz
  OCR1A =  49;                                     //  20000 Hz (alle 25us 1 Interrupt!)  
  
  // 20000 Hz
  //OCR1A =  29;                                     //  33333 Hz (alle 15us 1 Interrupt!)  
  
  sei();
// ============================================


  // Eingang Pin 3 Interrupt einrichten
  cli();                    // Interrupt verbieten
  PCICR  |= 1<<  PCIE2;     // Interrupt für Pins an Port D erlauben
  PCMSK2 |= 1<<  PCINT19;   // zusätzlich Pins auswählen: Interrupt für Pin 3 erlauben
  sei();                    // Interrupt erlauben
  Serial.println("\n\nStart:");
}


// ______________________________________________________________________________________________
// Wird immer wieder neu aufgerufen = Hauptprogramm
float Freq;

void loop() {
unsigned long p;
float fp;
  p=Period;
  if (p>0) {
    Serial.print("Perioden-Dauer(us): ");
    fp=(float) 2.0*p/SAMPLES;
    Serial.print(fp);
    Freq=(1000000.0 / fp);
    Serial.print(" Frequenz: ");
    Serial.println(Freq);
  }
  Period=0;
  delay(1000);
}