ich habe folgendes Problem in meiner Programmierung. ( sicherlich ein Anfängerfehler ... )
Mittels IR Refelexlichtschranke möchte ich die Drehzahl meines Propellers auf einem Motor - Propellerprüfstand messen. (Ausgabe in Umdrehungen pro Minute )
Eine volle Propellerumdrehung ( in meinem Fall ein 2 Blatt Propeller ) ergibt dann ein Signal mit 3 steigenden Flanken.
Nach der 3. Flanke weiß ich dass sich der Propeller einmal vollständig gedreht hat.
Sie Software habe ich wie folgt geschrieben.
Leider stimmen die Drehzahlwerte nicht überein.
Eventl liefert die micros() nicht genaue Zeitwerte oder ich hau mir die Berechnung zusammen, weil ich die Periodendauer als double deklariert habe.
Die Daten von Drehzahl lasse ich dann via RS232 an eine PC durch den Arduino senden und wenn ich die Werte nachträglich auswerte oder mit LogView visualisiere merke ich, dass ich starke periodische Schwankungen habe,welche auf ungenaue Zeitmessungen rückführbar wären. ( Schwankungen bis ca 800upm bei nicht veränderter Motordrehzahl )
Wäre der Lösungsweg der Drehzahlmessung ( Periodendauer einer vollen Umdrehung bzw von x Umdrehungen ) nicht einfacher über einen Hardwarecounter des Atmel Chips zu gehen?
int Drehzahlmessung() //Funktion für Ermittlung Drehzahl Propeller
{ //=======================================================================
uint8_t i;
uint8_t state;
uint8_t prevPegel, pegelNow;
uint8_t nrFlanken;
uint32_t timeNow;
uint32_t timeStart;
double startTime;
double endTime;
state = WARTE_ERSTE_FLANKE;
prevPegel = digitalRead(nRPM);
nrFlanken = 0;
timeStart = micros();
timeNow = timeStart;
//
// Schleife endet bei
// Zeitüberschreitung (keine Pulse am Eingang)
// wenn eine entsprechende Anzahl Pulse detektiert wurde
//
while( ThrottleVal >=2 && state != MESSUNG_FERTIG )
{
timeNow = micros();
pegelNow = digitalRead(nRPM);
switch( state )
{
case WARTE_ERSTE_FLANKE:
//
// Warten auf die erste steigende Flanke
// ist die erkannt, dann beginnt das Messintervall
//
if( prevPegel != pegelNow && pegelNow == HIGH )
{
startTime = timeNow;
state = WARTE_DRITTE_FLANKE;
}
break;
case WARTE_DRITTE_FLANKE:
//
// Messung läuft gerade
// Nach der 3.ten erkannten steigenden Flanke wird abgebrochen
// die 3.te steigende Flanke nach der ersten ist die hier:
//
// +---+ +---+ +----+ +----+ +----+
// | | | | | | | | | |
// --+ +----+ +----+ +----+ +----+ +----
// ^ ^ 1 2 3
// | | |
// | | Ende der Messung
// | Beginn der Messung
// |
// Einstieg in die Funktion
//
// Die 3.te steigende Flanke kennzeichnet den Begin des
// 4.ten Pulses. Ein 2 Blatt Prop hat daher 2 Umdrehungen
// gemacht.
//
if( prevPegel != pegelNow && pegelNow == HIGH )
{
nrFlanken++;
if( nrFlanken == 3 )
{
endTime = timeNow;
state = MESSUNG_FERTIG;
}
}
break;
}
prevPegel = pegelNow;
}
if( state == MESSUNG_FERTIG )
{
Drehzahl = 60/((endTime - startTime)/2000000); // aus endTime und startTime die Drehzahl
// n = f * 60 = 1/T *60 = 60/T = 60 / (( T/2 / 10000000 )) Umrechnung von µs auf sec und dann noch durch 2 weil innerhalb von T der Prop 2 Umdrehungen gemacht hat.
}
else
Drehzahl = 0;
return Drehzahl;
}
Ich würde in einer winzigen while auf den Flankenwechsel warten, grade mal while(digitalRead...==LOW..., oder aber, noch besser, das mit einem Interrupt realisieren. Letzteres wäre wohl am genauesten.
ok, mit attachInterrupt(interrupt, function, mode) habe ich den genauen bezug der Flankenerkennun geschaffen.
Wie messe ich nun die Zeit innerhalb einer zB High Periode?
Wieso versteifen sich immer alle, die eine Drehzahl messen wollen, die Impulse pro Zeiteinheit messen zu wollen und nicht die Zeit zwischen 2 Impulsen (Lücke zwischen den Blättern des Propellers) oder die Zeit eines Impulses und der Pause zwischen 2 Impulsen.
Vorteil dieses Ansatzes:
Man kann mit pulseIn() arbeiten, kein interrupt, kein millis() und keine falschmessung weil der Code zu langsam ist und Impulse nicht zählt.
Die Drehzahlmessung ist sehr schnell und umso schneller je größer die Drehzahl ist.
uwefed:
Man kann mit pulseIn() arbeiten, kein interrupt, kein millis() und keine falschmessung weil der Code zu langsam ist und Impulse nicht zählt.
War auch mein erster Gedanke, den ich aber verworfen habe, weil ich im Irrglauben war, dass da die Dauer in Millisekunden ausgegeben wird. Hab aber eben in die Referenz geschaut, es sind tatsächlich Mikrosekunden! Damit sollten gute Ergebnisse zustandekommen. Man könnte auch immer 10 aufeinanderfolgende Messungen zusammenfassen und mitteln. Oder besser 8 oder gleich 16, Bitshiften geht schneller als Dividieren.
Besteht bei pulseIn() nicht die Schwierigkeit, erstmal den "Gegenteiligen" Zustand am Pin zu treffen?
Also wenn ich z.b. die Dauer eines HIGH-Impulses messen will, sollte ich pulseIn() aufrufen, wenn gerade LOW am Pin anliegt, damit die Funktion den Anfang des HIGH-Impulses nicht verpasst.
Rufe ich pulseIn() einfach ohne Prüfung auf, kann es ja sein, das mein Pin gerade HIGH ist (irgendwie klingt das komisch, wenn ein Pin HIGH ist ) und es wird ein zu kurzer Impuls gemessen.
Ok, die Gefahr besteht doch nicht. Leider ist das aus der Beschreibung unter http://arduino.cc/en/Reference/PulseIn nicht wirklich ersichtlich.
Im Code selbst sieht man das aber:
// convert the timeout from microseconds to a number of times through
// the initial loop; it takes 16 clock cycles per iteration.
unsigned long numloops = 0;
unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;
// wait for any previous pulse to end
while ((*portInputRegister(port) & bit) == stateMask)
if (numloops++ == maxloops)
return 0;
// wait for the pulse to start
while ((*portInputRegister(port) & bit) != stateMask)
if (numloops++ == maxloops)
return 0;
// wait for the pulse to stop
while ((*portInputRegister(port) & bit) == stateMask) {
if (numloops++ == maxloops)
return 0;
width++;
}
// convert the reading to microseconds. The loop has been determined
// to be 20 clock cycles long and have about 16 clocks between the edge
// and the start of the loop. There will be some error introduced by
// the interrupt handlers.
return clockCyclesToMicroseconds(width * 21 + 16);
Es wird also erst gewartet, bis ein gerade laufender Puls beendet ist, um dann den nächsten vollständigen zu messen.
Spannend ist auch, das die Funktion nicht mit Zeiten arbeitet, sonder anhand der verbrauchten Taktzyklen die Zeit berechnet. Damit ist die Funktion sehr genau.
Offensichtlich kann man die Genauigkeit noch ein wenig steigern, wenn man vor dem Aufruf noch die Interrupts verbietet. Damit sollte man aber vorsichtig sein, da damit diverse Sachen wie z.b. Timer nicht weitergezählt werden. Jenachdem wie lang die gemessenen Pulse sind, kann man sich hier die nächsten Fallen bauen
zu meiner vorherigen Frage, was genau war der Fehler in meinem Ursprungsprogramm eurer Meinung nach?
danke für die toller Unterstützung die ich hier bekomme!!!