Forum: Mikrocontroller und Digitale Elektronik Periodenmessung


von ab2501 (Gast)


Angehängte Dateien:

Lesenswert?

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:
1
unsigned int t_speicher = 0;            //globale Variable
2
.
3
.
4
.
5
void __attribute__((__interrupt__)) _IC1Interrupt(void){
6
7
unsigned int t, dauer;
8
   
9
    t=IC1BUF;                                  //Buffer auslesen
10
    IFS0bits.IC1IF = 0;                     //Interrupt-Bit zu Null setzen
11
    if(t > t_speicher)
12
        dauer = t - t_speicher;          //Berechnung
13
    else
14
        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!

: Bearbeitet durch User
von Thosch (Gast)


Lesenswert?

Die PNGs funktionieren nicht...
Es gibt nur 'ne Fehlermeldung:
1
Die Grafik "http://www.mikrocontroller.net/attachment/205355/Abbildung_2.png"
2
kann nicht angezeigt werden, weil sie Fehler enthält.

Firefox 26.0, WinXP

von Thosch (Gast)


Angehängte Dateien:

Lesenswert?

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...

von ab2501 (Gast)


Lesenswert?

hey, danke fürs konvertieren..

und hier der Code als Text:
1
// Initialize the Input Capture Module
2
IC1CONbits.ICM = 0b00;
3
IC1CONbits.ICTMR = 1;
4
IC1CONbits.ICI = 0b01;
5
IC1CONbits.ICM = 0b011;
6
// Disable Input Capture 1 module
7
// Select Timer2 as the IC1 Time base
8
// Interrupt on every second capture event
9
// Generate capture event on every Rising edge
10
// Enable Capture Interrupt And Timer2
11
IPC0bits.IC1IP = 1;
12
IFS0bits.IC1IF = 0;
13
IEC0bits.IC1IE = 1;
14
// Setup IC1 interrupt priority level
15
// Clear IC1 Interrupt Status Flag
16
// Enable IC1 interrupt
17
// Capture Interrupt Service Routine
18
    unsigned int timePeriod= 0;
19
    void __attribute__((__interrupt__)) _IC1Interrupt(void)
20
    {
21
    unsigned int t1,t2;
22
    t1=IC1BUF;
23
    t2=IC1BUF;
24
    IFS0bits.IC1IF=0;
25
    if(t2>t1)
26
     timePeriod = t2-t1;
27
    else
28
     timePeriod = (PR2 - t1) + t2;
29
}

: Bearbeitet durch User
von Ulrich (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

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?

von ab2501 (Gast)


Lesenswert?

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?

von Ulrich (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

Ok, vielen Dank! hast mir echt sehr geholfen:)

von ab2501 (Gast)


Lesenswert?

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?

von Ulrich (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von ab2501 (Gast)


Lesenswert?

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..

von Ulrich (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von ab2501 (Gast)


Lesenswert?

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..

von Ulrich (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

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..

von Ulrich (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

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.

von ab2501 (Gast)


Lesenswert?

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.

von Ulrich (Gast)


Lesenswert?

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.

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.