Forum: Mikrocontroller und Digitale Elektronik STM32F105 3x Input Capture mit Timer1


von Bernd (Gast)


Lesenswert?

Hallo Forum,

ich nutze 3 Channel von Timer1 eines STM32F105 um Frequenzen bis 1kHz zu 
messen. Ich verwende die direkte Methode mit Capture Compare Interrupts, 
da ich jede Flanke zählen muss und auch den Dutycycle benötige.
Mein Problem ist, dass ich immer wieder Sprünge in der Frequenz um + 
oder - einen Überlauf habe. Timer 1 hat einen eigenen Update Interrupt 
und die Capture Compare Channel münden in einem Sammelinterrupt. Das 
bereitet mir Probleme bei der Ermittlung der passenden Überläufe bzw. 
der Synchronisation zwischen dem gespeicherten Capture Wert und der 
zugehörigen Anzahl Overflows. Der Timerwert wird bei einer Flanke in 
einem Register zwischen gespeichert, aber wie bekomme ich den 
zugehörigen Überlaufwert? Die Reihenfolge, wie die Interrupt Flags 
gesetzt werden, muss ja nicht zur Aufrufreihenfolge der Interrupt 
Funtkionen passen.
Aktuell hat der Update Interrupt eine höhere Priorität als der ander 
Interrupt. Außerdem zähle ich die Überläufe (jeweils 1 Counter pro 
Channel) im Update Interrupt nur hoch, wenn das entsprechende CC 
Interrupt Flag nicht gesetzt ist. Der CC Interrupt ist auch etwas 
länglich, da ich aktuell sofort die Frequenz berechne und im worst case 
alle 3 Kanäle hintereinander in einem Interrupt abgearbeitet werden 
könnten. Das ist nicht schön, passt m.M. aber nicht zu dem +-1 Überlauf 
Problem.
Wie löst man das sauber? Insbesondere auch mit 3 Kanälen

Kann man mit der viel zitierten reziproken Frequenzmessung auch den DC 
bestimmen? Kann ich mit 4 Channeln des Timers 3 Signale auswerten, also 
mit einer gemeinsamen Torzeit arbeiten? So ganz klar ist mir das noch 
nicht.

Gruß
Bernd

von Bernd (Gast)


Lesenswert?

Der Knackpunkt ist doch, wie stelle ich fest, welches Interrupt Flag 
zuerst gesetzt wurde, wenn beide sehr kurz nacheinander gesetzt wurden 
und es nicht eindeutig ist, in welcher Reihenfolge die Interrupt 
Funktionen ausgeführt werden...

von Bernd (Gast)


Lesenswert?

Der Timer läuft mit 72MHz durch. Aber selbst wenn er langsamer läuft, 
kann nie ausgeschlossen werden, dass Flanke und Überlauf zeitlich so nah 
beieinander liegen, das beide Flags gesetzt sind, aber die Reihenfolge 
unklar ist.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Bernd schrieb:
> Mein Problem ist, dass ich immer wieder Sprünge in der Frequenz um +
> oder - einen Überlauf habe.
Arbeite nicht mit absoluten Zählerwerten, sondern mit der Differenz 
zum letzten ausgelesenen Zählerwert. Dann "rechnet" sich der Überlauf 
von allein heraus.

So wie das z.B. beim Arduino und seinen millis() gemacht wird:
https://forum.arduino.cc/index.php?topic=285280.msg2000555#msg2000555

von Bernd (Gast)


Lesenswert?

Lothar M. schrieb:
> Arbeite nicht mit absoluten Zählerwerten, sondern mit der Differenz
> zum letzten ausgelesenen Zählerwert. Dann "rechnet" sich der Überlauf
> von allein heraus.

Hallo Lothar,

danke für deine Antwort.
ich verstehe nicht, wie in einer Differenz von zwei Zählerwerten mehrere 
Überläufe enthalten sein sollen?

von m.n. (Gast)


Lesenswert?

Die Interrupts von Capture und Überlauf müssen die gleiche Priorität 
haben. Sonst wird das nichts!
Ein Beispiel mit TIM9 findest Du hier: 
Beitrag "reziproker Frequenzzähler mit STM32F4Discovery"

Sieh Dir "void TIM1_BRK_TIM9_IRQHandler(void)" an.

von Bernd (Gast)


Lesenswert?

Genau das mit den zwei Interrupts macht mir noch Probleme.
Ich versuche das gerade gegen einander zu verriegeln, aber noch ohne 
Erfolg.

Der Reziprokzähler scheint ja von dir (m.n.) zu sein.
Kann ich damit mehrere Kanäle eines Timers gleichzeitig als 
Frequenzeingang nutzen? Und kann ich irgendwie zusätzlich den duty cylce 
bestimmen?

Habe an dem Beispiel aber jetzt verstanden, wie ermittelt wird, welches 
Flag zuerst war.
1
if((TIM9->SR & TIM_SR_UIF) && (zeit_low < 0x8000))    // evtl. Ueberlauf T9 noch offen?
Mit dem <0x8000 ergibt sich, dass zuerst der Überlauf und dann die 
Flanke aufgetreten sein muss. Clever.

Nur wo wird "ueberlauf" zurück gesetzt und warum werden 2 Port Pins 
konfiguriert, aber nur ein Capture Channel verwendet?

von m.n. (Gast)


Lesenswert?

Bernd schrieb:
> Nur wo wird "ueberlauf" zurück gesetzt und warum werden 2 Port Pins
> konfiguriert, aber nur ein Capture Channel verwendet?

Ein Capture-Eingang wird mit Vorteiler / 8 der andere direkt verwendet, 
auch wenn er im Beispiel ausmaskiert ist.

Mit Pulsweite habe ich auch etwas gemacht: 
http://mino-elektronik.de/FM_407/fmeter_407.htm#a5

Weiter oben zu dem Beipiel findest Du noch einen 4-kanaligen Zähler, und 
ganz unten mein 10-stelliges 'Spitzenmodell' ;-)

von Bernd (Gast)


Lesenswert?

Du benutzt anscheinend zwei Timer Kanäle für einen Pin Eingang, um 
Frequenz und DutyCycle zu bestimmen. Kann man nicht einen Timer Kanal 
nutzen und immer die zu triggernde Flanke umkonfigurieren? Dann könnte 
man doch mit einem Timer 3 Frequenzeingänge realisieren.

von m.n. (Gast)


Lesenswert?

Bernd schrieb:
> Kann man nicht einen Timer Kanal
> nutzen und immer die zu triggernde Flanke umkonfigurieren?

Bei 500 kHz bestimmt nicht mehr. Für 1 kHz probiere es aus, wie es bei 
kleinen Pulsweiten funktioniert.

von Bernd (Gast)


Lesenswert?

Also ich habe die Version von hier 
http://mino-elektronik.de/progs/STM32F4xx/pwm_f407/f_mess.c mit 
Umkonfigurieren der Flanke anstelle zweier interner Kanäle mit fester 
Flankenzuordnung ausprobiert. Das scheint aber nur im  Bereich 20% bis 
80% Dutycycle zu funktionieren. Ansonsten gibt es sporadische 
Frequenzsprünge nach unten. Das hätte ich noch nicht so früh erwartet. 
In den Randbereichen <5% und >95% wird es dann erwartungsgemäß deutlich 
schlimmer.

von Bernd (Gast)


Lesenswert?

Es gibt ja 4 Fälle zu betrachten, wenn das CaptureCompare und das 
TimerUpdate Flag quasi gleichzeitig gesetzt werden. 2 Davon sind 
problematisch und bedürfen ggf. einer gesonderten Betrachtung.
Ein Fall wird "offensichtlich" im Post vorher verlinkten Code behandelt 
(Im CC Interrupt und Zähler <0x8000).
Müsste man nicht eigentlich auch im Update Interrupt überprüfen, ob das 
CC Flag gesetzt ist?
Im Fall, dass beide Flags (Update und CC) "gleichzeitig" auftreten und 
zuerst die Update Interrupt Funktion ausgeführt wird und gleichzeitig 
der gespeicherte Zählerwert >0x8000 ist bedeutet doch, dass CC Interrupt 
zuerst da war, nun aber die Update Interrupt Funktion vorher ausgeführt 
wird. Dann ist der Überlaufzähler nach dem ++ für den zugehörigen 
Zählerwert einen zu groß.
Oder ist dieser Fall irgendwie in der Zeitdifferenz enthalten und führt 
nur bei der direkten Frequenzmessung zu Problemen?

von Bernd (Gast)


Lesenswert?

Ach so vergessen:
20% bis 80% bei 1kHz Eingangsfrequenz auf einem STM32F105 mit 72MHz

von m.n. (Gast)


Lesenswert?

Das Umschalten von Flanken ist immer problematisch. Wie Du es genau 
gemacht hast, ist ja nicht zu sehen.

Wenn Du schon alles mit einem Timer erledigen möchtest (meine Empfehlung 
wäre ein passender STM32 mit ausreichend Timern), dann würde ich mit 
beiden Flanken einen Capture-Interrupt auslösen und den Wert je nach 
aufgetretener Flanke in zwei separaten Variablen summieren. Das 
Verhältnis dieser Variablen ergibt dann das Tastverhältnis.

Alternativ können ein paar Timer die Funktion "Capture+Clear", mit der 
die Pulsweite einer Periode erfaßt werden kann. Wie weit man dort mit 16 
Bit Registern auskommt hängt von den Anforderungen und dem Vorteiler des 
betreffenden Timers ab.

Ansonsten kann ich empfehlen, passende breakpoints zu setzen und Flags 
und Register auf plausible Werte zu prüfen.

von Bernd (Gast)


Lesenswert?

Hallo Michael (m.n.)
Danke für deine Antwort.
Der Vorteil der Verwendung mehrerer Timer wäre doch, dass man keine 
Umschaltung der Flanken benötigt, richtig? Da immer 2 Kanäle pro Eingang 
benutzt werden können und so die Flanken für die DC Messung nicht 
umgeschaltet werden müssen, sondern je Kanal fest eingestellt sind.
Man muss dabei natürlich noch auf die Verteilung achten, also es können 
nur Kanal 1&2 und Kanal 3&4 entsprechend gekoppelt werden, so dass man 
dann pro Timer an 2 Frequenzeingänge die DutyCycle Messung nutzen kann. 
Oder gibt es weitere Vorteile? Vielleicht noch die individuelle 
Anpassung des Vorteilers bei sehr hohen Frequenzen.
Leider sind die anderen Timer bei mir bereits belegt (PWM).
Zu "Capture&Clear" habe ich beim STM32F105 und Timer1 nichts gefunden.

Gruß
Bernd

Das Umschalten der Flanken mache ich durch direktes setzen des 
Registers.
1
void TIM1_UP_IRQHandler(void) {
2
  // Update-IRQ bei Überlauf von T1
3
  TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
4
5
  ueberlauf++;
6
  mess_dauer++;
7
}
8
9
void TIM1_CC_IRQHandler(void)
10
{
11
  static uint32_t anzahl, anzahl_alt, zeit, zeit_alt, zeit_1, zeit_2;
12
  uint16_t temp;
13
  static uint8_t cc1_edgeTrigger = 1;        // 1: positive Flane, 0: negative Flanke
14
15
  if(TIM1->SR & TIM_SR_CC1IF) {     // CC1
16
    TIM1->SR = ~TIM_SR_CC1IF;
17
18
    FreqInputs[2].Pulscount++;
19
20
    if (cc1_edgeTrigger) {
21
      // positive Flanke
22
      cc1_edgeTrigger = 0;
23
      TIM1->CCER |= (uint16_t)TIM_CCER_CC1P;  // 1: inverted -> set capture on falling edge
24
25
      anzahl++;
26
      temp = TIM1->CCR1;
27
      zeit = temp + ueberlauf * 0x10000;         // capture-reg lesen: untere 16bit
28
      if((TIM1->SR & TIM_SR_UIF) && (temp < 0x8000)) {    // evtl. Ueberlauf T8 noch offen?
29
        zeit += 0x10000;                        // nur, wenn capture-int + overflow-int gleichzeitig !
30
      }
31
32
      if(mess_status == AUSLESEN) {               // Ergebnisse synchron zum Eingangsimpuls auslesen+ablegen
33
        mess_ereignisse = anzahl - anzahl_alt;    // Anzahl der Impulse
34
        anzahl_alt = anzahl;                      // fuer naechste Differenzbildung
35
        mess_zeit_1 = zeit - zeit_alt;            // eff. Messzeit
36
        zeit_alt = zeit;                          // fuer naechste Differenzbildung
37
        mess_zeit_2 = zeit_2;                     // Anteil der pos. Phase
38
        zeit_2 = 0;                               // wird wieder geloescht
39
        mess_status = AUSWERTEN;                  // Daten fertig fuer Auswertung
40
      }
41
    }
42
    else {
43
      // negative Flanke
44
      cc1_edgeTrigger = 1;
45
      TIM1->CCER &= (uint16_t)(~((uint16_t)TIM_CCER_CC1P));  // 0: non-inverted -> set capture on rising edge
46
47
      temp = TIM1->CCR1;
48
      zeit_1 = temp + ueberlauf * 0x10000;       // capture-reg lesen: untere 16bit
49
      if((TIM1->SR & TIM_SR_UIF) && (temp < 0x8000)) {   // evtl. Ueberlauf T8 noch offen?
50
        zeit_1 += 0x10000;                      // nur, wenn capture-int + overflow-int gleichzeitig !
51
      }
52
      zeit_2 += zeit_1 - zeit;                   // pos. Pulsweiten addieren
53
    }
54
  }
55
56
}

von Bernd (Gast)


Lesenswert?

Hallo Michael,

müsstest du im Update Interrupt nicht eigentlich auch den oben 
beschriebenen Sonderfall betrachten? Also wenn beide Flags quasi 
gleichzeitig auftreten, aber der Update Interrupt zuerst ausgeführt wird 
und der Zählerwert >0x8000 ist. Dann muss noch der alte Overflowwert für 
die Rechnung genommen und der alte Wert auch gespeichert werden. 
Natürlich muss der globale Overflowzähler trotzdem einen hoch gezählt 
werden.
Da das Auslesen des CCRx Registers implizit das CC Flag zurück setzt, 
muss man hier aber aufpassen. Es wird dann nicht mehr der CC Interrupt 
ausgeführt.

Bei 100 oder 20 Messungen pro Sekunde bekomme ich immer mal wieder einen 
positiven und einen negativen Ausreißer. Das passt zu dem oben 
beschriebenen Sonderfall. Bei größerer Messzeit ist das vermutlich aber 
vernachlässigbar.

Bei direkter Frequenzmessung muss dieser Sonderfall aber auf jeden Fall 
beachtet werden!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.