Go Down

Topic: Hardwarecounter für Frequenzmessung (Read 916 times) previous topic - next topic

ecsaa

Hi,
ich will mit dem Arduino Uno eine Frequenz messen.
Gibt es die Möglichkeit mit dem Arduino einen der Hardwarecounter des ATmega zu benutzten?

Udo Klein

Kommt auf die Frequenz an. Aber prinzipiell ja, solange Du unter 1 MHz bleibst geht das sogar ziemlich gut.
Check out my experiments http://blog.blinkenlight.net

ecsaa

Schön ...? aber wie komme ich an die Hardwarecounter ran?


ecsaa

danke! mehr wollte ich nicht wissen.

heweb

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

Code: [Select]

// ______________________________________________________________________________________________
// ______________________________________________________________________________________________
//
// 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);
}



heweb

... und hier ist mein Arduino-Frequenzmessgerät bis 6 MHz !

Es kann auch als Zeit-Messgerät zur Bestimmung von ausführungszeiten im us-Bereich genutzt werden.

Code: [Select]

// ______________________________________________________________________________________________
// ______________________________________________________________________________________________
//
//
// (C) Helmut Weber 2/2015
//
// Frequenzmessgerät 1kHz bis 6MHz
//
// Für Fortgeschrittene:
//
// An Pin 5 werden positive Pulse per Hardware gezählt
// Innerhalb einer vorgegebenen Zeit gezählte Pulse werden
// dann als Frequenz ausgegeben
//
// ______________________________________________________________________________________________
// ______________________________________________________________________________________________




//                             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));
};



unsigned int TIM16_ReadTCNT1( void )
{
unsigned char sreg;
unsigned int i;
/* Save global interrupt flag */
sreg = SREG;
/* Disable interrupts */
cli();
/* Read TCNT1 into i */
i = TCNT1;
/* Restore global interrupt flag */
SREG = sreg;
return i;
}

void TIM16_ClearTCNT1( void )
{
unsigned char sreg;
unsigned int i;
/* Save global interrupt flag */
sreg = SREG;
/* Disable interrupts */
cli();
/* Read TCNT1 into i */
TCNT1=0;;
/* Restore global interrupt flag */
SREG = sreg;
}




void setup() {
  Serial.begin(115200);
  pinMode(2,OUTPUT);
 
 
  // Timer 1 wird zum Zähler von Pulsen an Pin 5 (TO1)
  TCCR1A =0;
  TCCR1B =0;
  TCCR1B |=( (1<<CS10) | (1<<CS11) | (1<<CS12)  );  // Counter: external clock rising edge
  TCNT1 =0;
 

}


/*
// Messung von Befehlsausführungszeiten
void loop() {
unsigned long m;
unsigned int cnt, tim;
char buf[20];
unsigned long i;
  cnt=TIM16_ReadTCNT1();
  tim=1000000/cnt;
  Serial.println(cnt);
  Serial.print("pro loop: "); 
  sprintf(buf,"%d.%d us\n",tim/10,tim%10);
  Serial.print(buf);
  m=micros();
  m+=100000;                            // Messung für 100000us
  TIM16_ClearTCNT1();
  // Messung in 100 ms = 10000us
  // Die Pulse werden hier selbst erzeugt.
  while (micros()<m) {
    //BIT_SET(&PORTD,2);                  // 3.5us / loop
    //BIT_CLEAR(&PORTD,2);
    //    oder
    digitalWrite(2,HIGH);               // 13.5 us
    digitalWrite(2,LOW);
  }
  delay(100);
}
*/
// Messung von Frequenzen

unsigned int x=1;

void loop() {
unsigned long m;
unsigned int cnt, tim;
char buf[20];
unsigned long i;

  cnt=TIM16_ReadTCNT1();
  delay(100);
  dtostrf( (float)cnt/(float)x, 8,3,buf);
  Serial.print(buf); Serial.println(" kHz  ");  //Serial.print(cnt); Serial.print("  "); Serial.println(x);
  if (cnt<3000)   { if (x<1000) x*=10; }
  if (cnt>30000) { if (x>1) x/=10; }
  m=micros();
 
  if(TIFR1&(1<<TOV1)) { if (x>1) x/=10; TIFR1 |= (1<<TOV1); }
  // 100KHz -6MHz
  //m+=10000;
 
 m+=(unsigned long)x*(unsigned long)1000;
 
  TIM16_ClearTCNT1();
  // Messung in 100 ms = 10000us
  // Die Pulse werden hier selbst erzeugt.
  while (micros()<m) {
  }
}




Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy