1.6 🚧 Frequenz und Periodendauer

[de.wikipedia.org/wiki/Frequenz 🔗]

Die Frequenz f eines Signals ist die Anzahl der Wiederholungen pro Sekunde.
Im Beispiel ein Rechtecksignal. Die Periodendauer T ist dabei z.B. die Zeit von steigender zur steigender Flanke.

Um die Frequenz zu messen kann z.B. die Anzahl der steigenden Taktflanken in einer Sekunde gezÀhlt werden.
Bei niedrigen Frequenzen wird eher die Periodendauer T gemessen.
Oft ist auch die LĂ€nge des positiven und negativen Impulses von Interesse.

Auf dem Baseshield ist ein Frequenzgenerator mit NE555 verbaut, dessen Frequenz wir nun messen wollen. Dazu muss neben der Taste PA10 ein Jumper gesetzt werden, damit der Ausgang mit PA1 verbunden wird. Mit dem Poti unter dem LCD-Display kann die Frequenz verÀndert werden.

Frequenz und Periodendauer
Frequenz und Periodendauer

Frequenz messen

  • Eine ISR zĂ€hlt in einer Variablen zaehler die Positiven Taktflanken
  • Ein Timer löst jede Sekunde eine ISR aus:
    • Der Wert von zaehler enthĂ€lt die Frequenz und wird gespeichert
    • zaehler wird wieder auf 0 gesetzt.

VervollstĂ€ndigen Sie den Vorgabecode…

Frequenz messen
Frequenz messen
#define FREQUENZ_PIN PA1 // Frequenz zu messen
static HardwareTimer mytimer = HardwareTimer(TIM3);  // Timerinstanz sowie Timerauswahl
volatile unsigned long zaehler=0;  // Flanken zÀhlen
volatile unsigned long frequenz=0; // ermittelte Frequenz

void isr_Messen(){    // Jede Sekunde aufrufen
  ...
  Serial.printf("Frequenz %d Hz \n",frequenz);
}
void isr_Zaehlen(){   // zÀhlt bei jeder steigenden Taktflake
  ...
}
void setup(){
  Serial.begin (9600); //Serielle kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
  attachInterrupt (digitalPinToInterrupt (FREQUENZ_PIN), isr_Zaehlen, RISING); // steigende Taktflanken zÀhlen
  ...                                  // ISR jede Sekunde
  mytimer.attachInterrupt(isr_Messen); // Timer ISR einstellen
  mytimer.resume();  // Timer aktivieren
}
void loop(){}
Lösung
#define FREQUENZ_PIN PA1 // Frequenz zu messen
static HardwareTimer mytimer = HardwareTimer(TIM3);  // Timerinstanz sowie Timerauswahl
volatile unsigned long zaehler=0;  // Flanken zÀhlen
volatile unsigned long frequenz=0; // ermittelte Frequenz

void isr_Messen(){    // Jede Sekunde aufrufen
  frequenz = zaehler; // gemessene Frequenz sichern
  zaehler=0;
  Serial.printf("Frequenz %d Hz \n",frequenz);
}
void isr_Zaehlen(){   // zÀhlt bei jeder steigenden Taktflake
  zaehler++;
}
void setup(){
  Serial.begin (9600); //Serielle kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
  attachInterrupt (digitalPinToInterrupt (FREQUENZ_PIN), isr_Zaehlen, RISING); // steigende Taktflanken zÀhlen
  mytimer.setOverflow(1000000, MICROSEC_FORMAT);// ISR jede Sekunde
  mytimer.attachInterrupt(isr_Messen); // Timer ISR einstellen
  mytimer.resume();  // Timer aktivieren
}
void loop(){}

Periodendauer messen

Bei niedrigen Frequenzen wird die Zeit zwischen den Flanken gemessen.
Dazu wÀre eine gute Zeitbasis z.B. ”s zÀhlen praktisch. Ich möchte 2 LösungsansÀtze vorstellen.

  1. Einen Timer mit 1 MHz laufen lassen und dessen ZĂ€hlwert verwenden.
  2. Die Arduino-Funktion micros() verwenden, wie funktioniert sie genau?
  3. Die Hardware die Periodendauer messen lassen, kenne ich von AVR, mĂŒsste mit STM32 auch möglich sein?
Periodendauer messen
Periodendauer messen

1. Timer mit 1 MHz zum Messen laufen lassen

Notwendige Befehle stehen nicht in der Formelsammlung. Infos siehe:
[github.com/stm32duino/Arduino_Core_STM32/wiki/HardwareTimer-library]

  • Ein Timer zĂ€hlt mit 1 MHz die Mikrosekunden (CNT).
  • Der Overflowwert (ARR) ist maximal eingestellt, damit er nicht vorzeitig auf 0 gesetzt wird. (PrĂŒfen, ob das notwendig ist)
  • Eine ISR wird bei steigender Taktflanke von PA1 ausgelöst.
    • Dabei wird der Timerwert in einer Variable periode gespeichert.
    • Der Timerwert wird wieder auf 0 gesetzt.
#define FREQUENZ_PIN PA1 // Frequenz zu messen
static HardwareTimer mytimer = HardwareTimer(TIM3);  // Timerinstanz sowie Timerauswahl
volatile unsigned long periode=0; // ermittelte Periodendauer

void isr_Periode(){ // bei steigendenr Taktflake aufrufen
  periode= mytimer.getCount(); // Timer Wert speichern
  mytimer.setCount(0); // Timer auf 0 setzen
}
void setup(){
  Serial.begin (9600); //Serielle Kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
  attachInterrupt (digitalPinToInterrupt (FREQUENZ_PIN), isr_Periode, RISING); // steigende Taktflanken zÀhlen
  mytimer.setOverflow(0xFFFF,TICK_FORMAT);// auf Maximum setzen
  mytimer.setPrescaleFactor(32); // 32MHz Frequenz durch 32 Teilen -> 1”s
  mytimer.resume(); // Timer starten
}
void loop(){
  Serial.printf("Periodendauer %d ”s \n",periode);
  delay(1000);
}

2. Mit micros() Zeiten messen

micros() steht nicht in der Formelsammlung Info: [arduino.cc/reference/en/language/functions/time/micros/]

micros() zĂ€hlt die Mikrosekunden seit Systemstart in einer uint32_t Variablen. Beim STM32 werden dabei SystemTicks verwendet. [www.mystm32.de/doku.php?id=systemtickc 🔗]

Info: getCurrentMicros()
uint32_t getCurrentMicros(void){
  uint32_t m0 = HAL_GetTick();
  __IO uint32_t u0 = SysTick->VAL;
  uint32_t m1 = HAL_GetTick();
  __IO uint32_t u1 = SysTick->VAL;
  const uint32_t tms = SysTick->LOAD + 1;

  if (m1 != m0) {
    return (m1 * 1000 + ((tms - u1) * 1000) / tms);
  } else {
    return (m0 * 1000 + ((tms - u0) * 1000) / tms);
  }
}

Aufgaben: Nach welcher Zeit springt der micros() wieder auf 0?
Welche Auswirkung hat dies auf die Messung wenn der Sprung auf 0 wÀhrend der Messung geschieht?

#define FREQUENZ_PIN PA1 // Frequenz zu messen
volatile unsigned long periode = 0; // Periodendauer

void isr_Periode(){ // bei steigendenr Taktflake aufrufen
  static unsigned long zeitmarke = 0; // persistente Variable
  unsigned long zeitneu = micros();   // Zeit jetzt
  periode = zeitneu-zeitmarke; // Periodendauer speichern
  zeitmarke = zeitneu;
}
void setup(){
  Serial.begin (9600); // Serielle kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
  attachInterrupt (digitalPinToInterrupt (FREQUENZ_PIN), isr_Periode, RISING); // steigende Taktflanken
}
void loop(){
  Serial.printf("Periodendauer %d ”S \n",periode);
  delay(1000);
}

Impulsdauer messen

Nun soll die Impulsdauer des positiven und negativen Impulses gemessen werden, also wie lange ist das Signal High und wie lange ist es Low. Die Arduino-API bietet dafĂŒr eine Funktion pulseIn() an.

Arduino pulseIn()

Erstellen Sie mit pulseIn() ein Programm, dass die positive und negative ImpulslĂ€nge im Sekundentakt auf der seriellen Schnittstelle ausgibt.[www.arduino.cc/reference/en/language/functions/advanced-io/pulsein/ 🔗]

Lösungsvorschlag
#define FREQUENZ_PIN PA1 // Frequenz zu messen
unsigned long impulsdauerPos=0; // positive Impulsdauer
unsigned long impulsdauerNeg=0; // negative Impulsdauer

void setup(){
  Serial.begin (9600); // Serielle kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
}
void loop(){
  impulsdauerPos=pulseIn(FREQUENZ_PIN,HIGH);
  impulsdauerNeg=pulseIn(FREQUENZ_PIN,LOW);
  Serial.printf("Impulsdauer pos %d ”s neg %d ”s\n",impulsdauerPos,impulsdauerNeg);
  delay(1000);
}

Schauen Sie sich die Definition von pulseIn() an (rechte Maus und “Go to Definition”).

Aufgabe Entfernungsmesser mit zweistelliger Ausgabe..
Nachteil von pulseIn() erkennen..

Impulsdauer mit ext. ISR und micros() messen

Die Impulsdauern eines Signals ohne Unterbrechungen zu messen ist nicht schwer:
Ein externer Interrupt wird bei jeder Flanke des Signals ausgelöst und die vergangene Zeit seit der letzen Flanke der jeweiligen Impulsdauer zugeordnet.
VervollstÀndigen Sie den vorgegebenen Code:

#define FREQUENZ_PIN PA1 // Frequenz zu messen
volatile unsigned long impulsdauerPos=0; // Dauer positiver Impuls
volatile unsigned long impulsdauerNeg=0; // Dauer negativer Impuls

void isr_freqWechsel(){ // bei jeder Flanke aufgerufen
  static unsigned long zeitmarke = 0;
  unsigned long zeitneu = micros();     // Zeit jetzt gleich festhalten
  ...
}
void setup(){
  Serial.begin (9600); //Serielle kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
  attachInterrupt (digitalPinToInterrupt (FREQUENZ_PIN), isr_freqWechsel, CHANGE); // wechselnde Flanken
}
void loop(){
  Serial.printf("Impulsdauer pos %d ”s neg %d ”s\n",impulsdauerPos,impulsdauerNeg);
  delay(1000);
}
Lösung
#define FREQUENZ_PIN PA1 // Frequenz zu messen
volatile unsigned long impulsdauerPos=0; // Dauer positiver Impuls
volatile unsigned long impulsdauerNeg=0; // Dauer negativer Impuls

void isr_freqWechsel(){ // bei jeder Flanke aufgerufen
  static unsigned long zeitmarke = 0;
  unsigned long zeitneu = micros();     // Zeit jetzt gleich festhalten
  if(digitalRead(FREQUENZ_PIN)){        // war steigende Flanke
    impulsdauerNeg = zeitneu-zeitmarke; // Dauer negativer Impuls
  }else{                                // war fallende Flanke
    impulsdauerPos=zeitneu-zeitmarke;   // Dauer positiver Impuls
  }
  zeitmarke=zeitneu;
}
void setup(){
  Serial.begin (9600); //Serielle kommunikation starten
  pinMode(FREQUENZ_PIN, INPUT);
  attachInterrupt (digitalPinToInterrupt (FREQUENZ_PIN), isr_freqWechsel, CHANGE); // wechselnde Flanken
}
void loop(){
  Serial.printf("Impulsdauer pos %d ”s neg %d ”s\n",impulsdauerPos,impulsdauerNeg);
  delay(1000);
}