Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung


von Soisdas (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe immer noch ein Problem mit meiner Drehzahlmessung über eine 
Gabellichtschranke. Ich möchte den Wert erst ausgeben wenn 28 Messungen 
erfolgt sind. Das macht er auch aber auf der 7-Segmentanzeige kommen 
viel zu hohe Werte raus:

So in der Art:

0250
2401
8921
3456
9999

Aber warum? Die F_CPU habe ich richtig eingestellt.

Ich messe das gerade über einen Akkuschrauber in dem eine CD eingespannt 
ist mit einer Kerbe drin. Ist zwar aus der Hand raus nicht das stabilste 
aber die Werte sollten trotzdem annähernd passen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> if (Ausgabe==TRUE)
> // Das Display wurde mit den Ergebnissen der vorhergehenden

Den Fall würde ich mit einer Doppelpufferung der Zeitvariablen lösen, 
statt die ISR auf die Ausgabe Rücksicht nehmen zu lassen.

Die ISR misst stur durch. Am Ende einer Messung werden die Ergebnisse 
für die Anzeige bereitgestellt (StartTime und EndTime). Zu Beginn eines 
Anzeigezykluses liest die Anzeigefunktion diese Werte (atomar, siehe 
Interrupt) und schreibt sie sich in einen Zwischenpuffer.

> TIMSK = (1<<TICIE1) | (1<<TOIE1);   // Interrupts akivieren, Capture + Overflow
> ISR( TIMER1_OVF_vect )

Dieser Interrupt läuft bei dir nach dem Ende der Messzeit weiter! 
NrOverflows muss zum Zeitpunkt der Ausgabe nicht mehr den Zustand wie 
bei der Messung der EndTime haben. Durch das Verfahren oben kann man 
dieses Problem ohne Stoppen des Timers lösen. Zusätzlich zu StartTime 
und EndTime übergibt man der Anzeigefunktion auch eine Kopie von 
NrOverflows zum Zeitpunkt der Messung von EndTime. Bzw. man übergibt 
gleich den berechneten Wert (NrOverflows * 65536L + EndTime).

ICR1 würde ich in der Capture ISR einmal und zwar möglichst früh in eine 
temp. Variable auslesen und dann in der Fallunterscheidung kopieren. Das 
verringert Probleme mit unterschiedlichen Laufzeiten in den 
verschiedenen Zweigen.

Mehr habe ich nicht geprüft. Man muss nicht auf dem Dach arbeiten, wenn 
der Keller Löcher hat.

von Soisdas (Gast)


Lesenswert?

Angenommen ich nehme die Warteroutine in der ISR raus und übergebe die 
Overflows in einen Puffer:
1
ISR ( TIMER1_CAPT_vect )
2
3
 
4
{ 
5
6
  if( ErsteFlanke == TRUE )
7
  {
8
    StartTime = ICR1;      // Startzeit= ICR 1
9
    NrOverflows = 0;
10
    ErsteFlanke = FALSE;         // Die naechste Flanke ist das Ende der Messung
11
    
12
    
13
  }
14
15
  // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt
16
17
  else
18
  {
19
    EndTime = ICR1;        // Stopzeit für die Messung= ICR1
20
    Ausgabe = TRUE;        // Eine vollst?ndige Messung. Sie kann ausgewertet werden
21
    ErsteFlanke = TRUE;          // Bei der naechsten Flanke beginnt der naechste Messzyklus
22
    Puffer = NrOverflows;
23
    
24
  }
25
  
26
}

Dann springt er als nächstes in die Berrechnungsfunktion. Dort verbiete 
ich kurzzeitig Interrupts dann müsste er doch aufallefälle mal ein 
richtiges Ergebnis zusammenbekommen?! Dieses Teilprogramm haben schon 
zig Leute vor mir verwendet und bei denen hat es anscheinend geklappt 
der einzige Unterschied ist das ich kein LCD verwende sondern eine 
7-Segmentanzeige.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> Dieses Teilprogramm haben schon
> zig Leute vor mir verwendet und bei denen hat es anscheinend geklappt

Kann ich mir nicht vorstellen. Diesem Hinweis solltest du nach gehen:
>> (atomar, siehe Interrupt)

> Erg = (NrOverflows * 65536) + (EndTime - StartTime);

Kannst du diese Rechnung erklären?

> der einzige Unterschied ist das ich kein LCD verwende sondern eine
> 7-Segmentanzeige.

Dann würde ich mir die Rohwerte z.B. über RS232 ausgeben lassen. Und 
damit nachrechnen, ob die Berechnung der 7-Segmentanzeige funktioniert.

von Soisdas (Gast)


Lesenswert?

Angenommen bei einem CPU Takt von 8Mhz würde das Timeregister pro 
Sekunde  8.000.000x erhöht werden. Sprich es würden 122 Overflows 
stattfinden?

Erg = (122 * 65536) + (32500-20000)  = 8007892

Für Start&Endtime hab ich irgendeinen Wert genommen jenachdem wo der 
Timer gerade steht.

Erg = F_CPU / Erg;

Erg = 8000000/8007892 = 1

Drehzahl= Erg*60

Drehzahl= 1*60= 60 1/min

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Welchen Wert hat NrOverflows zum Zeitpunkt der Rechnung?

Nur eher zufällig den gleichen wie beim Zuweisen des Zeitstempel 2. 
Flanke (EndTime)...

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Also in der ISR eine Kopie von NrOverflows anlegen.

2. Hast du den Fall StartTime > EndTime mal gerechnet?

Ich würde so rechnen:

Erg = (NrOVF * 65536UL + EndTime) - (0 * 65536UL + StartTime)

oder verkürzt:

Erg = (NrOVF * 65536UL + EndTime) - StartTime

von Soisdas (Gast)


Lesenswert?

NrOverflow wird ja nach der ersten fallenden Flanke auf 0 gesetzt. 
Eigentlich bräuchte man den Wert wenn die 2. fallende Flanke ansteht.
Jetzt zählt er noch geringfügig weiter bis die Rechnung kommt das 
stimmt.
Ich hab versucht NrOverflow nach der 2. Flanke in eine Puffervariable zu 
packen leider änderte es nichts am Ergebniss


Gruss Ralph

von Soisdas (Gast)


Lesenswert?

Eigentlich sollte es nichts ausmachen da die Variablen vorzeichenlos 
sind sollte das gleiche rauskommen?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

1
#include <util/atomic.h>
2
3
volatile uint32_t Gemessene_Zeit;
4
5
ISR ( TIMER1_CAPT_vect )
6
{     
7
  static uint8_t ErsteFlanke = TRUE; // global entfällt
8
  if( ErsteFlanke == TRUE )
9
  {
10
    Ausgabe = FALSE; 
11
    StartTime = ICR1;
12
    NrOverflows = 0;
13
    ErsteFlanke = FALSE;  
14
  }
15
  else
16
  {
17
    EndTime = ICR1;
18
    Gemessene_Zeit = (NrOverflows * 65536UL + EndTime) - StartTime;
19
    ErsteFlanke = TRUE;    
20
    // Eine vollständige Messung. Sie kann ausgewertet werden
21
    Ausgabe = TRUE; 
22
  }
23
}
24
25
void Messwerte_auslesen(void)
26
{  
27
  if ( Ausgabe == FALSE ) 
28
    return;
29
30
  // http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html
31
  // mikrocontroller.net Artikel Interrupt
32
  ATOMIC_BLOCK(ATOMIC_FORCEON)
33
  {
34
    Erg = Gemessene_Zeit;
35
  }
36
37
  // Aus der Messzeit ergibt sich dann die Frequenz f = 1 / t
38
  // Drehzahl berechnen aus der Frequenz f * 60 sec = U/min
39
  zahl = (60UL * F_CPU) / Erg;
40
41
  // Über 28 Einzelmessungen mitteln
42
  Mittelwert += zahl;
43
  schleife++;
44
  if ( schleife == 28 )
45
  {
46
    drehzahl = Mittelwert / schleife;
47
    Mittelwert = 0;
48
    schleife = 0;
49
  }
50
51
  // Max. mögliche Anzeige begrenzen
52
  if ( drehzahl > 9999 )
53
    drehzahl = 9999;
54
}
55
56
main()
57
{
58
  ...
59
  while(1)
60
  {
61
    Messwerte_auslesen();  
62
    Messwerte_ausgeben();
63
  }  
64
}

Die Zahl der globalen volatile Variablen kann drastisch verringert 
werden. Volatile müssen sein: Gemessene_Zeit, Ausgabe und NrOverflows.

von Soisdas (Gast)


Lesenswert?

Hallo Stefan,

ich dank dir für deine Arbeit und den Code. Der sieht doch gleich viel 
sauberer aus und das mit dem atomaren Auslesen war mir auch völlig neu.

Ich hab aber noch das Problem das mir ziemlich oft die 9999 angezeigt 
wird oder auch bei langsame Drehzahlen viel zu hohe Werte.

Das kann ja eigentlich nur am Wert von NrOverflow liegen der viel zu 
hoch ist?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Prüfe die Datentypen deiner Variablen. Hier zwei Beispiele

Was passiert weil NrOverflows 8-Bit und nicht 16-Bit ist?

Gemessene_zeit ist mit NrOverflows 8-Bit max. 16777215. d.h. die 
Mindestdrehzahl ist 28 rpm.

Was passiert weil zahl signed und nicht unsigned ist?

Wenn Erg (= Gemessene_zeit) kleiner wird als 14648 (<1,83 ms, >32768 
rpm), wird zahl durch Integeroverflow negativ.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Derzeit (Mittelwert im Userprogramm) kann es vorkommen, dass man Werte 
verpasst, weil die ISR bereits eine neue Messung angefangen hat. Deine 
ursprüngliche Lösung hatte das Problem nicht, weil die ISR einfach nicht 
gemessen hat, so lange noch nicht ausgegeben wurde. auch nicht ideal.

Auch muss man beachten, ob man lange genug misst. Beispiel bei 28 rpm 
und Mittelwert aus 28 Messungen liegt ein gültiger Mittelwert frühstens 
nach 1 Minute vor!

IMHO würde es Sinn machen, die Mittelwertbildung in der ISR zu machen. 
Und günstig wäre eine gleitende Mittelwertbildung statt einer fixen.

Ich könnte mir gut vorstellen, dass die ISR z.B. Mittelwerte der Zeiten 
aus 2, 4, 8, 16,... Messungen anbietet. 2er Potenzen um die Divisionen 
schnell zu machen (Rechtsshift). Die aufwändigere Berechnung der 
Drehzahl und die Aufbereitung der Anzeige wird wie bisher im 
Userprogramm gemacht.

von Soisdas (Gast)


Lesenswert?

Hallo Stefan,

man bin ich betrunken. Schau mal in die Interrupinitialisierung.

"|="

Das im Zusammenhang mit dem ICN1 Filter und deiner Version des Programms 
bringt mir zumindest jetzt ganz passable Werte :)

Jetzt muss ich nur noch die Anzeige auf 0000 abfallen lassen wenn in 
einer bestimmten Zeit keine Interrupts mehr auftauchen.

Die Bohrmaschine hängt an einem FU und besitzt eine Abfallrampe von 
2sek.
Minimaldrehzahl = 200 1/min.

Da könnte ich theoretisch sagen wenn die Drehzahl < 200 = 0.
Aber wenn ein Not-Aus betätigt wird steht die Maschine abrupt.
Dann würde die Anzeige noch einen Wert anzeigen.

Wie löst man sowas am Besten?


Gruss Ralph

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Hmm, mich würde es stutzig machen, wenn überhaupt noch eine Anzeige zu 
sehen ist, wenn der Not-Aus gedrückt ist. Not-Aus heisst für mich: Da 
ist kein Saft mehr drauf. Nirgends.

Auch bei dem Nullen per Software unterhalb der Mindestdrehzahl 200 rpm 
wäre ich vorsichtig. Die 0 würde ich nur anzeigen, wenn das Teil sich 
wirklich < 1 rpm bewegt also NrOverflows > 7324 (d.h. NrOverflows 
auf uint16_t erweitern).

Das Unterschreiten der Mindestdrehzahl könnte man anzeigen, in dem man 
z.B. die Anzeige blinken lässt. Eine andere Methode wäre z.B. ---- 
anzuzeigen, aber nicht 0000. 0000 würde für mich heissen: Maschine dreht 
nicht mehr bzw. < 1 rpm.

> man bin ich betrunken. Schau mal in die Interrupinitialisierung.
> "|="

Passiert. Bei mir im Simulator ist das nicht schlimm. Das Testprogramm 
läuft dennoch. Merke: Simulieren ist nicht Testen.

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.