www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung


Autor: Soisdas (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Soisdas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Angenommen ich nehme die Warteroutine in der ISR raus und übergebe die 
Overflows in einen Puffer:
ISR ( TIMER1_CAPT_vect )

 
{ 

  if( ErsteFlanke == TRUE )
  {
    StartTime = ICR1;      // Startzeit= ICR 1
    NrOverflows = 0;
    ErsteFlanke = FALSE;         // Die naechste Flanke ist das Ende der Messung
    
    
  }

  // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt

  else
  {
    EndTime = ICR1;        // Stopzeit für die Messung= ICR1
    Ausgabe = TRUE;        // Eine vollst?ndige Messung. Sie kann ausgewertet werden
    ErsteFlanke = TRUE;          // Bei der naechsten Flanke beginnt der naechste Messzyklus
    Puffer = NrOverflows;
    
  }
  
}


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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Soisdas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welchen Wert hat NrOverflows zum Zeitpunkt der Rechnung?

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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Soisdas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Soisdas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eigentlich sollte es nichts ausmachen da die Variablen vorzeichenlos 
sind sollte das gleiche rauskommen?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#include <util/atomic.h>

volatile uint32_t Gemessene_Zeit;

ISR ( TIMER1_CAPT_vect )
{     
  static uint8_t ErsteFlanke = TRUE; // global entfällt
  if( ErsteFlanke == TRUE )
  {
    Ausgabe = FALSE; 
    StartTime = ICR1;
    NrOverflows = 0;
    ErsteFlanke = FALSE;  
  }
  else
  {
    EndTime = ICR1;
    Gemessene_Zeit = (NrOverflows * 65536UL + EndTime) - StartTime;
    ErsteFlanke = TRUE;    
    // Eine vollständige Messung. Sie kann ausgewertet werden
    Ausgabe = TRUE; 
  }
}

void Messwerte_auslesen(void)
{  
  if ( Ausgabe == FALSE ) 
    return;

  // http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html
  // mikrocontroller.net Artikel Interrupt
  ATOMIC_BLOCK(ATOMIC_FORCEON)
  {
    Erg = Gemessene_Zeit;
  }

  // Aus der Messzeit ergibt sich dann die Frequenz f = 1 / t
  // Drehzahl berechnen aus der Frequenz f * 60 sec = U/min
  zahl = (60UL * F_CPU) / Erg;

  // Über 28 Einzelmessungen mitteln
  Mittelwert += zahl;
  schleife++;
  if ( schleife == 28 )
  {
    drehzahl = Mittelwert / schleife;
    Mittelwert = 0;
    schleife = 0;
  }

  // Max. mögliche Anzeige begrenzen
  if ( drehzahl > 9999 )
    drehzahl = 9999;
}

main()
{
  ...
  while(1)
  {
    Messwerte_auslesen();  
    Messwerte_ausgeben();
  }  
}

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

Autor: Soisdas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Soisdas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.