projektplanung Wellendrehzahl messen

Hallo zusammen,

ich möchte mit einem pro mini die Drehzahl einer Welle mittels Hallsensor ermitteln und die Anzahl der Umdrehungen sowie die aktuelle Drehzahl pro Minute auf ein LED display (einfaches IIC oled) ausgeben. Die Drehzahl der Welle liegt zwischen 0 bis 4500 Umdrehungen. Auf der Welle gibt es zwei Magneten.

Da ich Anfänger bin, hab ich den Hallsensor und das Display erstmal einzeln getestet. Dabei ist mir aufgefallen das das Schreiben auf das Display ca 24ms dauert. Die Zeit zwischen zwei Impulsen vom Hallsensor könnte aber mit 13ms deutlich darunter liegen. Ich vermute es wäre sinnvoll durch einen Timer die Displayaktualisierung auszulösen.
Dazu zwei Fragen: Hab ich richtig verstanden, wenn die ISR aufgerufen ist, wird keine Unterbrechung dieser Funktion durch einen anderen Interrupt ausgelöst?
Angenommen die Aktualisierung der Anzeige dauert wegen irgendwelcher Arbeit 40ms und wird durch einen Timer ausgelöst, was passiert dann mit den Impulsen die in dieser Zeit maximal vom Hallsensor kommen? Werden diese Ereignisse irgendwie gespeichert und dann später abgearbeitet oder werden die schlichtweg ignoriert?

Viele Grüße, Kay

Warum willst du das Display über 70 mal in der Sekunde aktualisieren? Wer soll sich das Gezappel ansehen? Im Gegenteil, ich würde immer über mehrere Messungen einen gleitenden Mittelwert bilden und das Display höchstens 2..3 mal pro Sekunde mit einem neuen Wert versehen.

Evtl hast du dann sogar noch Zeit, z.B. eine Kurve mit den 10 vorherigen Werten darunter zu zeichnen ...

Hi

4500 Umdrehungen/min, 2 Magnete -> 9000 Impulse pro Minute, 150 die Sekunde.
Oder 6,67µsms bis zum nächsten Signal.
Dazwischen passen nicht wirklich viele Befehle.

Wenn Du die Umdrehungen 'auslagerst' - denke Da an einen Zähler, Der z.B. alle 32 Impulse 'toggelt' - so hast Du deutlich mehr Zeit und Dir gehen keine Umdrehungen verloren, Die Du zählen willst.
(halt in 32er Schritten - sofern man den Zähler am Ende des Programm nicht noch ebenfalls ausliest)

In dieser Zeit kannst Du zwei/drei Flanken ausmessen und die aktuelle Geschwindigkeit ermitteln.
Wie genau sind die Magnete ausgerichtet? Wenn hier die Winkel nicht ganz passen, bekommst Du schwankende Werte.
Diese Werte werden alle 500ms auf dem Display aktualisiert - weniger Gezappel, man kann die Werte ggf. sogar 'mitzählen'.

MfG

EDIT
Rechenfehler (µs statt ms) korrigiert.
Daraus ergibt sich, daß doch schon einige Befehle zwischen die einzelnen Ereignisse passen.
Näheres wird dazu in den folgenden Posts erörtert.
Danke der Korrektur.

Guten Abend zusammen,

geht vieleicht in deine Richtung:

const uint8_t pwm_q = 9;
const uint8_t tacho = 2;
uint32_t serial_ms = 0;
//funk. fuer mittelwertbildung
float mittelwert(const float akt_upm)
{
	static float upm[4];
	//Serial.println("DEBUG:");
	for (uint8_t i = 3; i > 0; i--)
	{
		upm[i] = upm[i - 1];
		//Serial.print("i = "); Serial.println(i);
		//Serial.print("upm ="); Serial.println(upm[i]);
	}
	upm[0] = akt_upm;
	return ((upm[0] + upm[1] + upm[2] + upm[3]) / 4.0);
}

void setup() 
{
	Serial.begin(115200);
	Serial.setTimeout(250);
	while(!Serial){	}
	pinMode(tacho, INPUT_PULLUP);
	pinMode(pwm_q, OUTPUT);
}

void loop() 
{
	static bool tacho_block = true;
	static uint32_t tacho_us = 0;
	if (Serial.available() > 0)
	{
		serial_ms = millis();
		String serial_in = Serial.readStringUntil('\n');
		analogWrite(pwm_q, constrain(serial_in.toInt(), 0, 255));
		//int serial_in = Serial.parseInt();
		//analogWrite(pwm_q, constrain(serial_in, 0, 255));
		serial_ms = millis() - serial_ms;
		Serial.print("Eingegebener Wert: ");
		Serial.println(serial_in);
	}
	if (!digitalRead(tacho) && !tacho_block)
	{
		uint32_t akt_us = micros();
		tacho_block = true;
		float upm = 60.0 / float(akt_us - tacho_us) / 1000000.0;
		Serial.println(mittelwert(upm), 2);
		tacho_us = akt_us;
	}
	else if (digitalRead(tacho) &&tacho_block)
	{
		tacho_block = false;
	}
}

bei mehr als "nur" 2000 Impulsen/min solltest du meines Erachtens nach Interrupts verwenden und den loop sehr sehr schmal halten.

Gruß
grillgemuese :slight_smile:

postmaster-ino:
4500 Umdrehungen/min, 2 Magnete -> 9000 Impulse pro Minute, 150 die Sekunde.
Oder 6,67µs bis zum nächsten Signal.

150 Impulse / Sekunde
1000ms / 150 = 1 Impuls pro 6,68ms

Hallo,

Wie Doc schon schieb Postmaster hat sic um 3 Zehnerpotenzen verrechnet. Du must 6,8 ms erfassen können da sollte kein Problem sein. Kannst Du also mit mircos() messen.

ich würde die Zeit für z.B 10 positive Flanken messen und daras das Messergrbniss für die Drehzahl berechen.Dann hast Du schon mal unterschiede durch die Geometrie der Anordnung von den Magneten so ziemlich gemittelt. Messzeiten ergeben sich dann z.B mit .

bei 100U/min = 3,3Hz ca. 3s Messzeit bei 10 Perioden

bei 4500u/min = 150 Hz ca. 67ms Messzeit bei 10 Perioden

wenn Du die 10 Flanken erkannt hast rechnest Du um und zeigst es an , dann gehts zurück und du misst wieder neu.

Damit die Messzeit nicht zu lang werden kann , z.B bei 10 U/min wäre sie unter den Umständen ja 30 s lang könnte man den Messvorgang auf eine max Zeit begrenzen. Dann wird es aber etwas kompliziert weil Du dann nur die zeit zwischen ganzen Perioden zur Auswertung verwenden darfst.

Heinz

Rentner:
Damit die Messzeit nicht zu lang werden kann ...

... würde ich eine Sekunde die Impulse zählen und bei zwei Magneten durch zwei geteilt ergibt die Drehzahl in U/Sek. Dann jede Sekunde das Display auffrischen. Das geht natürlich auch mit einer halben oder zwei Sekunden.

Die Impulse würde ich wegen deren vermutlicher Kürze immer per Interrupt, hier hat er m. E. seine Berechtigung, einlesen. In der ISR nur den Zähler inkrementieren, Auswertung und Anzeige in loop.

Hallo, meine 2 cent dazu:

Da ich eher von der Elektronikerseite komme, ist der Drehzahlmesser für mich ein Frequenzmesser,
hier mit der Besonderheit 2 Impulse pro Umdrehung.
Kleine Frequenzen (Drehzahl) misst man über die Impulsabstände, zum Beispiel H-Flanke zur nächsten H-Flanke.
Hier wären 2 Messungen mit anschließender /2 Teilung notwendig, dann ist egal, ob die Magnete symetrisch sitzen.
Hohe Frequenzen werden über ein Gate gemessen, also Impulse pro fester Messzeit 1s oder 0,1s oder was auch immer (hier /2). Die Gatetime ist dann auch gleich die Refreshzeit für das Display.
Wenn man verspielt ist, kann man noch eine Bargraph Anzeige mit LEDs basteln.

Gruß André

agmue:
... würde ich eine Sekunde die Impulse zählen und bei zwei Magneten durch zwei geteilt ergibt die Drehzahl in U/Sek. Dann jede Sekunde das Display auffrischen. Das geht natürlich auch mit einer halben oder zwei Sekunden.

Die Impulse würde ich wegen deren vermutlicher Kürze immer per Interrupt, hier hat er m. E. seine Berechtigung, einlesen. In der ISR nur den Zähler inkrementieren, Auswertung und Anzeige in loop.

Hallo, sorry es wird nicht ohne Messung der Periodendauer gehen, Tormessung über 1 s geht nicht wirklich.

100 U/min = 1,666 U/s bei zwei Magneten = 3,3333 Hz

bei einer sekunde Messzeit misst du 3 Impulse und das ergibt eine sehr schlechte Auflösung( ca. 30%) das nexte Ergebniss kann 4 sein. .

3 Imp/s -> 90 U/min
4 Imp/s-> 120U/min

dazwischen gibt es nichst.

Gruß Heinz

Hallo,

der TO hat die Wahl selbst etwas zu programmieren und dabei viel zu lernen.
Oder er nimmt was fertiges FreqMeasure Library, for Measuring Frequencies in the 0.1 to 1000 Hz range, or RPM Tachometer Applications

Hallo,

muss noch was zum Thema Puls Pausen Verhältniss loswerden und den Zeiten dazu :wink:

die Magnete haben eine bestimmte Grösse und sollen vom Hallsensor erkannt werden. Der Hallsensor liefert ein H signal solange der Magnet darunter ist. Damit ist das Puls/Pausen Verhältniss nicht 1:1 sondern irgendwie anders und ergibt sich aus dem Winkel für den H erkannt wird.

Wenn man jetzt mal von 10 Grad ausgeht, ich kenne aber die Geometrie nicht, sieht es in Etwa so aus:

4500U/min = 4500/60 = 75U/s

75Hz => 13ms 1 Periode entsp. 360Grad für einen Magnet.

H Impuls Zeit = 13/360*10 = 0,36ms

Bei Verwendung von 2 Magneten sieht es dann so aus.

75U/s *2 =150Hz

150 Hz -> 6,6 ms

D.h alle 6,6 ms kommt ein H und muss erkannt werden und das H signal ist 0,36ms lang. Man sollte also versuchen die Magnete so anzubringen das das Tastverhältniss nicht zu unglücklich wird. Bei einer gegebene Grösse kann man sie also weiter innen näher der Welle anbringen um es zu verbessern.

Heinz

Hallo,

... wenn man den Pegel auswertet. Wenn man die Flanke auswertet kann der Magnet Wochen vorm Sensor stehen. :slight_smile:
Ansonsten richtig erkannt.

Hallo,

nur mal als Gedankenstütze wenn ich das zu Fuss programmieren müßte. Irgendeinen PCINT mit Flankenerkennung programmieren.
Dazu einen 16 Bit Timer mit Prescaler 256 einstellen. Dann kann ich ohne Überlauf Zyklen bis 1,04s messen.
Daraus ergibt sich dann eine Auflösung von 16µs (256*62,5ns) pro Timercount.
Überlegungen basieren auf folgenden großzügigen Annahmen.

max. Drehzahl (5000x2) 10000 U/min = 166,67 U/s = 166,67Hz
Periodendauer min = 6,0ms

min. Drehzahl (50x2) 100 U/min = 1,67 U/s = 1,67Hz
Periodendauer max = 600ms

Man könnte bis runter auf 1Hz messen, was gemessene 60U/min (real 30U/min) bedeutet.

Hallo zusammen,

ok, vielen Dank für die zahlreichen Beiträge. Ich muss mich erstmal durch alle Beiträge ackern, ist nicht gerade mein Fachgebiet.
@Doc_Arduino: Du meinst nur die Impulse in einer festen Zeit zählen per Interrupt auf steigender Flanke und anschließend die Auswertung in loop() machen? Wenn ja was passiert wenn der Timer abgelaufen ist, dadurch die entsprechende Routine angetriggert wird und dann ein Impuls vom Hallsensor käme? Unterbricht dieser die vom abgelaufenen Timer angetriggerte Routine?

@postmaster-ino: Meinst du "im Hintergrund" die Zeit für z.B. 32 Impulse messen und nur dann auswerten? Die Ausrichtung der Magnete ist sicher nicht präzise, müsste man mal ausmessen bei konstanter Drehzahl...

Hi

Meine Idee geht in die Richtung, daß ein externer Counter o.Ä. das Zählen einer Anzahl an Impulsen übernimmt und wir eben an einem höheren Bit dieses Counter 'nur alle 32 Impulse' sehen.
Das reicht aus, um die Umdrehungen mitzuhalten, es geht Keine verloren - aber halt nur in 32er Schritten.
Was dem 'Gesamt-Weg' aber nicht abträglich ist.
Also extern, nicht 'im Hintergrund'.

Allerdings geschah diese Überlegung auf meinem Irrtum, daß wir nur 6µs Zeit hätten - da wir aber 6ms Zeit haben (die 1000-fache Zeit), sollte sich Das auch 'in Echtzeit' auf dem Arduino erledigen lassen.

Wenn wir eh mit einer ISR die Zeiten der Impulse erfassen (oder die Impulse pro Zeit), können wir dort auch einen volatile Zähler hoch zählen.

MfG

Hallo,

meine Überlegung basiert auf dem weitergeführten Gedanken von agmue. Man misst die Zeit zwischen 2 Flanken. Dazu liest man pro Flankenerkennung den Timercounter aus. Ermittelt jedesmal die Differenz usw. um letztlich auf die Drehzahl zu kommen. Per PCINT/ISR geht da nix verloren. Ein Timer läuft nie ab. Am Ende kommt der Überlauf, springt auf 0 und weiter gehts bis zum nächsten Überlauf. Der Überlauf spielt keine Rolle, solange der benötigte Zeitbereich die benötigte Zeitspanne abdeckt. Das Problem ist nicht die höchste Drehzahl, sondern die niedrigste Drehzahl die noch erfasst werden soll.

Ob du dann jeden Wert auswertest oder 10 oder 1000 sammelst oder einen gleitenden Mittelwert bildest ist dann am Ende fast nur noch Kosmetik.

Kay19:
@Doc_Arduino: Du meinst nur die Impulse in einer festen Zeit zählen per Interrupt auf steigender Flanke und anschließend die Auswertung in loop() machen?

Das war mein Vorschlag aus #6. Ich würde das machen, ohne einen Timer direkt anzusprechen, sondern mit micros(). Die ISR wird bei steigender oder fallender Flanke des Sensors aufgerufen, dort der Zähler inkrementiert. In loop nach Ablauf von beispielsweise 1000 Mikrosekunden den Zähler auswerten, anzeigen und auf 0 setzen. Bei dieser Methode bedeuten keine Impulse auch keine Drehzahl.

Natürlich kann man auch die Zeit zwischen zwei Impulsen, wie von den anderen favorisiert, messen. Dabei hat man das theoretische Problem, daß keine Drehzehl der unendlichen Meßzeit entspricht. Dies muß man abfangen. Wenn die (konstante) Zeit zwischen zwei Anzeigen gleich der Meßzeit ist, dann muß die Drehzahl 0 angezeigt werden.

Wenn Du Arduino-Timer kennenlernen möchtest, so wäre dies eine gute Gelehenheit. Sonst finde ich millis() oder micros() einfacher.

Alle Theorie ist grau. Wenn Du unentschlossen bist, beide Verfahren programmieren und vergleichen, dann das für Dein Projekt besser geeignete auswählen.

Hallo,

ich würde beide Methoden nutzen. Bei kleinen Drehzahlen, kommen zu wenige Impulse in der Messzeit herein. Hier ist die Messung des Abstands zwischen den Impulsen genauer. Bei großen Drehzahlen, kann man genauer ein Intervall z.B. 1s. messen und die Zahl der Impulse zählen. Stillstand ist, wenn z.B. 4s. kein Impuls mehr hereinkommt.

Um ein wenig Praxis in den Thread zu bringen, gibt es von Agmue gibt es schöne Interrupt-Testsketche:

1.) Impulse pro Sekunde

// UNO mit Messung an Pin2

const byte messpin = 2;
volatile unsigned long zaehler;

void setup() {
  Serial.begin(9600);
  pinMode(messpin,INPUT_PULLUP);
  attachInterrupt(0, messung, CHANGE);
}

void loop()
{
  delay(1000); // every second
  Serial.print("Zaehler: "); Serial.println(zaehler); 
}

void messung() {
  if (digitalRead(messpin)) zaehler++;
}

2.) Zeit zwischen Impulsen

// UNO
const byte messpin = 2;
volatile unsigned long highTime;
volatile unsigned long lowTime;
volatile unsigned long startmicros;

void setup() {
  Serial.begin(9600);
  attachInterrupt(0, messung, CHANGE);
}

void loop()
{
  delay(1000); // every second
  Serial.print("highTime: "); Serial.print(highTime); Serial.print("  ");
  Serial.print("lowTime: "); Serial.print(lowTime); Serial.print("  ");
  Serial.println();
}

void messung() {
  if ((PIND >> 2) & 1) {
    lowTime = micros() - startmicros;
  } else {
    highTime = micros() - startmicros;
  }
  startmicros = micros();
}

Hallo

Frequenzmessung ist ein schönes Thema, Ich hab mir auch mal Gedanken gemacht und meine hier bereits vorgeschlagene Idee realisiert. Periodendauer mehrere Perioden messen und daraus die Frequenz berechnen und auf einem LCD anzeigen.

Zunächst habe ich das ohne Interrupt realisiert , und dazu die positive Flanke des Signals ausgewertete. Zum Testen habe ich mit tone() eine Frequenz auf einen Ausgang gegeben und diese mittels einer Brücke auf meinen Messeingang gegeben. Die Messergebnisse waren ganz anständig, so bis etwa 1000Hz dann traten aber schon mal Aussreißer auf.

Also habe ich mich mit dem Thema Interrupt beschäftigt , hatte ich bisher noch nicht gemacht.

Hier also meine Lösung:

// Hardware UNO 
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

int fin = 2;   // Eingang Frequenz interrupt (UNO pin 2,3)
int maxperiode = 10;     // Anzahl Perioden für Messung
int periode;    // Periodenzähler

volatile unsigned long messzeit; // gemessene Zeit für n Perioden
unsigned long alt_t;    // Start Messzeit
unsigned long alt_int;   // micros letzter Interrupt
unsigned long alt_ref;  // letzte Refresh time der Anzeige

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  Serial.begin(9600);
  pinMode(fin, INPUT_PULLUP);
  
  // Interrupt einrichten
  attachInterrupt(digitalPinToInterrupt(fin), messen, RISING);
  periode = maxperiode;   // Startwert
}

void loop() {
  tone(5, 500); // Testfrequenz zum testen Brücke Pin2 auf Pin5
  
  if (millis() > alt_ref + 1000) { // refresh Zeit für Anzeige abgelaufen
    alt_ref = millis();
    anzeige();
  }
}

//------ ISR  Messfunktion -----------------------------
void messen() {
  alt_int = micros(); // letzte Zeit aktualisieren
  if (periode == maxperiode) {
    alt_t = micros();  // messen starten
  }

  if (periode == 0 ) {  // messen ist beendet
    messzeit = micros() - alt_t; // messen ende
    periode = maxperiode; // neu starten

  }
  else {  
    periode --; // Periodenzähler verringern
  }
}

// -----------Anzeige funktion----------------------
void anzeige() {
  float frequenz;
  float pdauer;
  pdauer = messzeit / 1000.0 / maxperiode;
  frequenz = 1000 / pdauer;

  if (micros()> alt_int +100000){ // Keine Frequenz erkannt.
    messzeit=0;
    pdauer=0;
    frequenz=0;
  }
  Serial.print("Messzeit ms "); Serial.print(messzeit / 1000);
  Serial.print(" Perodendauer ms "); Serial.print(pdauer, 4);
  Serial.print("  Frequenz Hz " ); Serial.println(frequenz);
  // -----Ausgabe auf LCD -----------------
  lcd.setCursor(0, 0);
  lcd.print ("P-Dauer ms "); lcd.print(pdauer);
  lcd.setCursor(0, 1);
  lcd.print ("Freq. Hz "); lcd.print(frequenz);

}

OK, aktuell zähle ich in der ISR die Impulse hoch und berechne die abgelaufene Zeit mit micros(). Auswertung dann in loop(). Das sollte für niedrigere Drehzahlen funktionieren. Die Auswertung nimmt ja auch noch Zeit in Anspruch und die Displayaktualisierung dauert ganze 24ms bisher. Mal sehen ob ich nur einen Teil des Displays aktualisieren könnte, also nur die Ziffer die sich tatsächlich ändert, dafür muss ich aber auch mehr rechnen.

Aktuell stehe ich leider noch auf dem Schlauch was den Vorschlag angeht Impulse in einer festen Zeit zu messen ohne einen Timer direkt anzusprechen. Ist damit gemeint in loop() mittels millis() und if() zu prüfen ob eine Zeit >= des Messintervalls vergangen ist? Falls ja wäre es ja kein festes Intervall, oder? Ein delay() möchte ich in jedem Fall vermeiden da die Rechenzeit ohnehin knapp sein könnte.

@Doc_Arduino: Verstehe ich das richtig den externen Interrupt durch den Hallsensor und um micros() zu vermeiden den Timercounter auslesen?

Viele Grüße, Kay