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
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...
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.
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
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?
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.
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?
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' ;-)
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.
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.
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.
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?
Ach so vergessen: 20% bis 80% bei 1kHz Eingangsfrequenz auf einem STM32F105 mit 72MHz
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.
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 | }
|
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.