Hallo,
ich möchte mit einem PIC33 die Periodendauer eines Rechtecksignals
bestimmen. Habe dazu auch ein passendes Beispiel im Datenblatt gefunden.
Allerdings wird hier nur von jedem zweiten Rechteck die Dauer bestimmt,
da nur alle zwei Captures (steigende Flanke) ein Interrupt ausgelöst
wird. Ich hätte die Berechnung aber gerne bei jedem Rechteck.
Habe das entsprechende Bit jetzt auch schon so angepasst, dass der
Interrupt bei jedem Capture auslöst. Wenn ich das richtig verstanden
habe, steht jetzt aber immer nur noch eine "Zeit" im Capture-FIFO (beim
ersten Capture ja auf jeden Fall). Könnte ich den zweiten Wert zur
Berechnung der Periode über einen globale Variable in die
Interrupt-Funktion bringen? Also:
dauer=(PR2-t1)+t2;//Berechnung wenn Timer überläuft
15
16
t_speicher=t;//neue Zeit speichern für nächsten Interrupt-Aufruf
17
}
Kann das so funktionieren, oder hab ich da noch irgendwelche Denkfehler
drin, bzw gibts eine bessere Lösung? Vielen Dank schonmal für die
Unterstützung!
O.K., gerade gesehen in IrfanView: Das sind keine PNG-Dateien, das sind
TIFF-Dateien mit der (falschen) Endung .PNG
Code wäre als Text wesentlich sinnvoller, als als Grafik.
Hab die Grafik mal ins PNG-Format konvertiert, wird gleich viel kleiner,
ohne Qualitätsverlust...
So ähnlich wie ganz oben kann das funktionieren. Es gibt aber noch 2
Kleinigkeiten:
Die globale variable sollte Volatile sein, damit der Compiler nicht zu
viel weg optimiert. Besser noch wäre noch als static in der ISR, sofern
die zeit nicht noch außerhalb benötigt wird.
Auch das Ergebnis sollte wohl eine volatile Variable werden - so fern es
außerhalb der ISR gebraucht wird.
Die Unterscheidung t2>t1 kann entfallen. Beim rechnen mit unsigned
zahlen werden die Überläufe auch so richtig berücksichtigt.
danke für die Antwort:)
möchte die berechnete Dauer direkt nach der Berechnung, also noch im
Interrupt, über einen SDO-Pin an einen DAC schicken.. dann kann ich sie
als static definieren, oder?
also die Zeit brauche ich nur innerhalb der Funktion.. deshalb
"static".. und die Dauer möchte ich an den DA-Wandler weitergeben.. dann
auch static oder kann ich die dann so lassen wie es jetzt ist?
Wenn man die Periodendauer nur in der ISR braucht, reicht eine normale
Variable - kann also so bleiben. Die kann auch "static" sein, muss es
aber nicht.
Das static ist vor allem für die zu merkende Zeit, damit die zwischen
den ISR aufrufen erhalten bleibt.
Ich muss das Thema leider nochmal aufgreifen.. Ich lasse den Timer, von
dem die Zeiten "gecaptured" werden, mit 2,5MHz zählen. So bekomme ich
bei meiner maximalen Sensorfrequenz noch 500 "Zählungen". Mein Problem
ist jetzt aber, dass ich ja auch gerne bei kleinen Frequenzen messen
möchte. Deshalb nutze ich einen 32bit Timer um ein Überlaufen bei
kleinen Frequenzen, bei denen ich ja dann deutlich weiter zähle, zu
verhindern. Trotzdem liefert mir mein Mikrocontroller bei kleinen
Frequenzen sinnlose Ergebnisse.
Habe eine Vermutung, dass es an den zu großen variablen des 32bit Timers
liegt. Meine Rechnung während des Interrupts sieht so aus:
unsigned long t, t_s;
float drehzahl_d;
unsigned int drehzahl;
float faktor;
t=IC1BUF;
drehzahl_d = faktor/(t-t_s);
t_s=t;
drehzahl = drehzahl_d; //Konvertierung in Int für SPI-Modul
über die Kehrwertbildung und den Faktor möchte die zugehörige Drehzahl
ausgeben. Die erste Frage wäre hier, ob der Controller (dsPIC33)
überhaupt mit unsigned long Variablen umgehen kann. Diese wären ja dann
groß genug um den 32bit-Timer Wert aufzunehmen. Falls das geht, wie
sieht es mit der Division und Subtraktion aus? Oder kann es noch andere
Gründe für den Fehler bei niedrigen Frequenzen geben?
Der µC selber kann nicht direkt mit 32 Bit Zahlen rechnen, aber der
Compiler kann passenden Code erzeugen das auchmit 32 Bit Zahlen
gerechnet wird.
Das Problem könnte eher beim Auslesen des 32 Bit Wertes aus den ICP
Registern liegen. Da bin ich mir nicht sicher ob es so einfach mit
t=IC1BUF geht (hängt von der Unterstützung durch den Compiler ab). Nicht
das da nur 16 Bit Werte ausgelesen werden.
Du scheinst Recht zu haben.. in der Anleitung steht, dass der 16bit Wert
des TImers gecaptured wird.. d.h. so kann meine Messung ja nicht
funktionieren bei geringen Frequenzen.. Welche Möglichkeit könnte man
denn noch verfolgen, um wirklich einen großen Frequenzbereich, bzw.
Drehzahlbereich, abzudecken. Ich würde aber gerne bei der Variante
bleiben bei der ich zwischen zwei steigenden Flanken die Zeit messe. Das
Zählen von Zähnen in einem festen Zeitintervall liefert zu wenig
Messwerte während einer Umdrehung.
2 Möglichkeiten
* den Timer langsamer laufen lassen, so dass deine Differenz nicht
größer als 65535 wird.
* zusätzlich zum Capturing noch die Timer Overflows mitprotokollieren.
Wenn der Capture Start Wert größer als der Endwert ist, werden zur
Differnz noch '( Anzahl Overflow - 1 ) * 65536' mit dazu gezählt. Ist
der Start Wert kleiner als der End Wert, dann werden (Anzahl Overflows *
65536) zur Differenz addiert.
Allerdings muss man hier aufpassen, wenn der Endwert 0 oder nahe 0 ist,
ob der entsprechende Overflow an dieser Stelle schon registriert wurde
oder nicht.
Nochmal Danke für die Antworten!! habe versucht die zweite Möglichkeit
umzusetzen. Dazu nutze ich jetzt zusätzlich zu meinem Capture-Interrupt,
in dem ich die Berechnung durchführe, einen Timer-Interrupt mit dem ich
die Overflows hochzähle. Problem ist aber bei dieser Variante, dass ich
ja während meiner Berechnung keine Overflows mehr zählen kann, da ich ja
dann in einem anderen Interrupt bin.
Wie wäre es denn, wenn ich ganz weg von dem Capture gehe und einfach
einen Interrupt nutze der auf steigende Flanken an einem Pin reagiert
und dann im Interrupt folgende Berechnung durchführe:
unsigned long t;
float faktor;
float drehzahl_f;
unsigned int drehzahl;
t = TMR2; //Timer auslesen
TMR2 = 0x00; //Timer wieder auf 0 setzen
drehzahl_f = faktor / t; //Drehzahl berechnen
drehzahl = drehzahl_f; //int-Variable für SPI (kann ich so von float
zu
int casten?)
In diesem Fall kann ich ja den Timer als 32bit-Timer zählen lassen und
habe somit einen größeren Wertebereich. Funktioniert die Division dann
auf jeden Fall? Ich nutze den C30-Compiler..
Man kann auch weiter die Capture Funktion nutzen. Das Problem mit dem
Überlauf ist bekannt und wurde ja auch schon angesprochen.
Den ggf. durch fast gleichzeitige Interrupts verschluckten Überlauf zu
erkennen ist gar nicht so schwierig: Es hängt vom µC ab, ob der Capture
oder der Überlauf Interrupt vorrangig ausgeführt werden falls beide
gleichzeitig anstehen. Ich kenne das von den AVRs, da wird der Cpature
Interrupt zuerst ausgeführt - es kann also vorkommen das der Überlauf
noch ausseht (sonst wäre es ggf. anders herum, das der Überlauf schon zu
früh dran war - das Prinzip ist aber das gleiche).
Den "verpassten" Überlauf Interrupt erkennt man in der ISR zum Capture
daran, dass das Interruptflag für den Überlauf gesetzt ist, und
gleichzeitig der Wert im Capture Register kleine ist (etwa < 1000, die
Grenze ist aber unkritisch), also erst nach dem Überlauf aufgetreten
ist. Das trifft nicht nur für den Endwert, sondern auch für den
Startwert zu, also immer wenn der per Software auf 32 Bit erweiterte
Timer ausgelesen wird.
Das ist auf jeden Fall schonmal eine gute Möglichkeit diese Fehler
abzufangen.. Danke:) Und ich kann ja einfach durch die Priorität des
Interrupts festlegen, ob Capture- oder Timer-Interrupt bevorzugt
ausgelöst wird.
Die einzige Schwierigkeit die ich jetzt noch sehe, ist wenn während
meiner Rechnung ein oder vielleicht auch mehrere Überläufe auftreten.
Die kriege ich ja dann nicht mit, wenn mein Capture-Interrupt die höhere
Priorität hat.. Gebe ich dem Capture-Interrupt die niedrigere Priorität,
wird vermutlich meine Rechnung ständig unterbrochen.. oder sehe ich das
falsch?
ab2501 schrieb:> Die einzige Schwierigkeit die ich jetzt noch sehe, ist wenn während> meiner Rechnung ein oder vielleicht auch mehrere Überläufe auftreten.
Richtig.
Daher: Rechnung kurz halten. Im wesentlichen wird in der ISR einfach nur
der Wert aus dem Capture Register geholt, die Differenz gebildet und das
wars dann auch schon. Die Drehzahl kann die Hauptschleife gemütlich
aussen rum ausrechnen.
Das kann noch nicht alle Problemfälle aussortieren, aber so gut wie
alle.
> Die kriege ich ja dann nicht mit, wenn mein Capture-Interrupt die höhere> Priorität hat.. Gebe ich dem Capture-Interrupt die niedrigere Priorität,> wird vermutlich meine Rechnung ständig unterbrochen.. oder sehe ich das> falsch?
Über welche zu messenden Frequenzen reden wir? Über welche Taktfrequenz
vom µC reden wir? Ist das daher 'ständig'?
Mir scheint du hast die 'Beamtenattitüde', der jammert, weil 2 mal in
der Woche der Bäcker im Büro vorbeikommt um Brötchen zu verkaufen und er
daher nicht arbeiten könne.
Ok.. das klingt gut. Hatte vorher ehrlich gesagt alle Berechnungen,
einschließlich Senden der Daten per SPI in dem Capture-Interrupt, aber
das mach ich dann jetzt alles in die Endlosschleife in der Main meines
Programms.
Danke für die Unterstützung..
Die Interruptpriorität, also die Möglichkeit die ISR vom Capture durch
den Overflow zu unterbrechen , hilft hier nicht weiter, worauf es
ankommt ist welcher Interrupt ausgelöst wird, wenn beide fast
Gleichzeitig auftreten, z.B. in der kurzen Zeit in der Z.B, im
Hauptprogramm Interrupts gesperrt sind. Ein overflow Interrupt der den
Capture Interrupt unterbricht stört sogar, weil der dann ggf. zu früh
ausgeführt wird.
Ob während der Rechnung (in der ISR) dann noch einmal ein Overflow oder
so auftritt stört nicht weiter - der wird ja erst danach ausgeführt. Die
Rechnung, selbst mit der Division mit Fließkommazahlen dauert ja auch
nicht so lange, schon gar nicht dass 2 Overflows kommen könten.
Die Frage ist eigentlich nur ob der Überlauf oder das Capture zu erst da
waren, wenn beide fast Gleichzeitig aufgetreten sind, und das kann man
gut am Capture Wert erkennen. Je nach µC bzw. Einstellungen muss man
halt die Abfrage für den Speziallfall entweder in die ISR vom Capture
(z.B. beim AVR) oder den Overflow (falls im Zweifelsfall erst der
Overflow ausgelöst wird) einbauen.
Nochmals vielen Dank für die Unterstützung.. ihr habt mir wirklich sehr
geholfen!!!
1
t=IC1BUF;
2
if(IFS0bits.T2IF==1&&t<1000)
3
ueberlauf=ueberlauf+1;
4
5
if(t_s>t)
6
d=t-t_s+(ueberlauf-1)*65536;
7
else
8
d=t-t_s+ueberlauf*65536;
9
10
ueberlauf=0;
11
t_s=t;
12
13
IFS0bits.IC1IF=0;
So sieht meine Zeiterfassung (Capture-Interrupt) jetzt aus. Zusätzlich
habe ich noch ein Timer-Interrupt, dass die Überlauf-Variable hochzählt.
Die Berechnung mach ich dann in meiner Main und es funktioniert alles
sehr gut. Lediglich bei hohen Frequenzen habe ich abundzu noch kleine
Ausreißer in meinem Messergebnis. Die stören mich nicht weiter, da sie
vernachlässigbar sind, aber es wäre natürlich trotzdem schön zu
verstehen woher sie kommen. Vielleicht hat ja noch jemand eine Idee..
Ein kleines Problem ist da noch in der Routine: Wenn der Speziallfall
mit dem noch ausstehenden Überlauf interrupt autritt, wird hier der
überlauf hochgezählt: für diese eine Zeit ist das OK, aber die Überlauf
ISR kommt dann direkt danach, und das ist dann zu viel. Da müsste man
also zusätzlich noch den anstehenden Überlauf Interrupt löschen, oder
laternativ nicht die glabale Variable hochzählen, sondern nur bei der
Berechnung einen dazu zählen.
Habe das jetzt auch noch angepasst, aber habe leider immer noch abundzu
kleine Ausreißer. Mich wundert nur, dass meine Ausgabe wirklich über
mehrere Sekunden total stabil und konstant ist und dann plötzlich ganz
kurz das Signal einmal abfällt. Dieses Verhalten wiederholt sich
regelmäßig, was ja noch für irgendeinen kleinen Sonderfall spricht, mit
dem ich momentan noch nicht korrekt umgehe.
Mir ist auch ehrlich gesagt der Sonderfall bei dem der Capture-Startwert
größer ist als der Endwert nicht ganz klar. Wieso muss ich da in der
Berechnung vom Überlauf noch 1 abziehen?
Nehmen wir mal an mein Timer läuft bei 20 über. Der Startwert ist 3 und
der Endwert 18, es gab 2 Überlaufe zwischen den Werten. Setze ich das in
die Formel "t - t_s + ü*20" ein ergibt das 55, was stimmt. Ist mein
Startwert jetzt hingegen 18 und der Endwert 3, muss bei der selben
Anzahl von Überläufen 25 herauskommen. "t - t_s + ü*20" ergibt den
richtigen Wert, "t - t_s + (ü-1)*20 ergibt 5.
Also eigentlich müsste die erste Formel beide Fälle abdecken. Was mich
nur verwirrt dabei, ist, dass meine Ausgabe deutlich stärker schwankt,
wenn ich diese Fallunterscheidung weglasse.
Ich glaube ich weiß woran es liegt.. Umstellen der Formel hat das
Ergebnis deutlich stabilisiert:
dauer = (ü-1)*20 + t - t_s
meine Vermutung ist, dass das Ergebnis vorher kurz während der
Berechnung negativ werden konnte. Das ist jetzt bei der neuen
Reihenfolge nicht mehr der Fall wenn die Rechnung von links nach rechts
durchgeführt wird.. kann das sein oder ist das totaler Quatsch?;)
Jetzt ist nur noch abundzu ein deutlicher Ausreißer in der
Ausgangsspannung des DA-Wandlers zu erkennen. Das liegt meiner Meinung
nach daran, dass meine Interrupts auch in die SPI-Kommunikation zwischen
Controller und Wandler "reinfunken". Ist so etwas grundsätzlich möglich
oder läuft SPI komplett über die Hardware und wird daher nicht von
Interrupts beeinträchtigt.
Ein Speziallfall, den man ggf. leicht vergisst ist, das die Kollision
von Capture Interrupt und Overflow sowohl beim Start als auch beim Stop
passieren kann. Entsprechend ist es besser auch die Zeit für den Start
gleich auf 32 Bit zu erweitern - die Fallunterscheidung nach ts>t
erübrigt sich dann, bzw. zeigt dann einen Überlauf ( Zeit zu groß) an.