Drehzahlmessung mit Arduino Nano

Hallo, ich mache gerade ein Projekt, wobei ich die Drehzahl von einem Rad abrufen muss. Ich habe mir einen Reedsensor mit Magnet gekauft und versuche die Drehzahl auf einem LCD Display anzuzeigen. Das mit dem Display funktioniert einwandfei, aber bei der Messung ist etwas faul. Der Reedsensor gibt , wenn der Magnet in der Nähe ist ein High Signal aus. Ich habe es versucht mit folgendem Program die Dauer zwischen zwei Impulsen zu messen, aber die Messung gibt mir manchmal Minuszahlen aus und laut messung ist der Impulsabstand wenn sich das Rad schneller dreht, länger, als wenn es sich langsamer dreht.

Hier der oben angesprochene Code:

int startzeit=0, messzeit=1000, timerOld=0, timer=0, zaehler;
void loop(){  

 if((micros()-startzeit)>=messzeit)
  {
    delay(200);
    lcd.clear();

    attachInterrupt(0, messung, RISING);
    float f=timer;
    Serial.println(timer);
    f=60/(f/1000000);
    //detachInterrupt(0);
    lcd.setCursor(0,1);
    lcd.print(f);
    zaehler = 0; //Frequenzzähler zurücksetzen
    startzeit = micros(); //Zeitpunkt der letzten Ausgabe speichern
    
  }
}

void messung(){
  zaehler++;
  timer=micros()-timerOld;
  timerOld=micros();
}

Beispiel ausgegebener Werte

32080
-26912
-12172
6472
6472
17248
31928
-15748
-15748
3288
21552
21552
-30188
-30188
-24688
-14728
-14728
3616
3616
32504
32504
-4632
-4632

Ich würde mich freuen, wenn mir wer helfen könnte.

Wenn du keine negativen Zahlen möchtest, solltest du auch keine erlauben.
Und sowieso ist der Datentype zu klein für micros().

Also: "Augen auf, bei der Datentype Wahl"

Was gefällt dir an pulseIn() nicht?

danke für die Rückmeldung.

pulseIn() gibt die Zeit zwischen zwei Impulsen un mikrosekunden zurück, oder?

dann sollte das Programm so funktionieren:

   float f;
    delay(200);
    lcd.clear();

    f=pulseIn(2, HIGH);
    f=60/(f/1000000);
    //detachInterrupt(0);
    lcd.setCursor(0,1);
    lcd.print(f);
    Serial.println(f);

Hi

Wenn willst Du wohl eher die Zeit erfassen, in Der Du den Magneten NICHT siehst.
Hier wirst Du die Zeit erfassen, Die der Magnet am Reed-Sensor ist.

Die Sache mit den Datentypen ist zu Dir durchgedrungen?

Dein Snipped - 'sollte so funktionieren' ... funktioniert Der auch so?
Könnte mir vorstellen, daß laufend 0 (Null) raus kommt.
(Wenn, ändere die 60 in 60.0, sonst einfach ignorieren)

MfG

postmaster-ino:
Hi

Wenn willst Du wohl eher die Zeit erfassen, in Der Du den Magneten NICHT siehst.
Hier wirst Du die Zeit erfassen, Die der Magnet am Reed-Sensor ist.

Die Sache mit den Datentypen ist zu Dir durchgedrungen?

Dein Snipped - 'sollte so funktionieren' ... funktioniert Der auch so?
Könnte mir vorstellen, daß laufend 0 (Null) raus kommt.
(Wenn, ändere die 60 in 60.0, sonst einfach ignorieren)

MfG

ich habe den float auf double geändert und das High auf Low und jetzt funktioniert es einwandfrei, danke für die Hilfe

float auf double

Da du offensichtlich einen AVR nutzt, ist das völlig egal.

Aber es zeigt, dass du dich noch nicht wirklich um Datentypen gekümmert hast....

Die Doku zu pulseIn() sagt:

Returns
the length of the pulse (in microseconds) or 0 if no pulse started before the timeout (unsigned long)

Demnach wäre unsigned long angemessen.

Oder?

Hallo,

schau mal hier, da habe ich das Ganze schon mal durchexerziert :wink:

Drehzahlmessung für Plattenspieler

Ulli

beeblebrox:
Hallo,

schau mal hier, da habe ich das Ganze schon mal durchexerziert :wink:

Drehzahlmessung für Plattenspieler

Ulli

Aber keiner kann das Ergebnis sehen. Keine Zugriffsrechte!

Der code mit pulseIn() hat einen kleinen Fehler. Man misst dabei die Dauer vom Ende eines Pulses bis zum Anfang des nächsten Pulses, sollte man nicht vom Ende eines Pulses bis zum Ende des nachfolgenden Pulses messen, um die Drehzahl richtig zu messen?

Man misst dabei die Dauer vom Ende eines Pulses bis zum Anfang des nächsten Pulses,

Ist diese Zeit nicht proportional zur Drehzahl?

Wenn ich das richtig verstehe, möchte der TO die absolute Drehzahl. Dazu braucht's die Zeit für eine ganze Umdrehung. Wäre das Puls/Pause Verhältnis über den gesamten Drehzahlbereich konstant, könnte man das rausrechnen. Aber ob man bei einem mechanischen Schalter davon ausgehen kann ? Das hängt natürlich dann auch noch vom zu erfassenden Drehzahlbereich ab.
Sicherer ist jedenfalls, immer von der gleichen Flanke aus zu messen. Wobei ich den Moment bevorzugen würde, wo das Reedrelais zum ersten mal auf den vorbeidrehenden Magnet reagiert. Dann kann einem sogar Prellen egal sein. Ich gehe mal davon aus, dass die Prellzeit deutlich kleiner ist, als die kützeste Zeit für eine Umdrehung :wink: .

Ich habe den Code so verändert, dass er von falleder Flanke zu fallender Flanke misst:

  f=pulseIn(2, LOW, 10000000);
  t=pulseIn(2, HIGH, 10000000);
  f=60000000.0/(f+t);

MicroBahner:
Wenn ich das richtig verstehe, möchte der TO die absolute Drehzahl. Dazu braucht's die Zeit für eine ganze Umdrehung. Wäre das Puls/Pause Verhältnis über den gesamten Drehzahlbereich konstant, könnte man das rausrechnen. Aber ob man bei einem mechanischen Schalter davon ausgehen kann ? Das hängt natürlich dann auch noch vom zu erfassenden Drehzahlbereich ab.
Sicherer ist jedenfalls, immer von der gleichen Flanke aus zu messen. Wobei ich den Moment bevorzugen würde, wo das Reedrelais zum ersten mal auf den vorbeidrehenden Magnet reagiert. Dann kann einem sogar Prellen egal sein. Ich gehe mal davon aus, dass die Prellzeit deutlich kleiner ist, als die kützeste Zeit für eine Umdrehung :wink: .

Wie kann man die Prellzeit ausblenden?

Man kann statt des Reedkontaktes auch einen Hallsensor montieren.
Der prellt dann nicht.
Grüße Uwe

dxaxhxix:
Ich habe den Code so verändert, dass er von falleder Flanke zu fallender Flanke misst:

  f=pulseIn(2, LOW, 10000000);

t=pulseIn(2, HIGH, 10000000);
 f=60000000.0/(f+t);





Wie kann man die Prellzeit ausblenden?

Meiner Meinung nach ist PulseIn für dich nicht das Richtige. Du willst ja eigentlich keine Pulsdauer messen, sondern immer von positiver Flanke zu positiver Flanke. ( ader auch negativ-negativ ). Außerdem ist PulseIn blockierend, was in deinem Fall eigentlich einer Dauerblockade gleichkommt.
Je nach Drehzahlen, um die es geht ( das solltest Du uns mal mitteilen ) wäre dein erster Ansatz schon gar nicht so schlecht. Im Interrrupts misst Du die Zeit seit der letzten Flanke. Wenn diese Zeit zu klein ist (Prellen) ignorierst Du den Interrupt. Ansonsten nimmst Du die Zeit als Umdrehungszeit, und startest eine neue Messung.
Das Einrichten des Interrupts machst Du aber im setup(), und deine Zeitvariable für den Interrupt musst Du als globale Variable und als volatile definieren. Wenn Du im loop() auf die Zeitvariable aus dem IRQ zugreifst, musst Du den IRQ sperren, damit Du keine ungültigen Zwischenwerte liest.

MicroBahner:
Meiner Meinung nach ist PulseIn für dich nicht das Richtige. Du willst ja eigentlich keine Pulsdauer messen, sondern immer von positiver Flanke zu positiver Flanke. ( ader auch negativ-negativ ). Außerdem ist PulseIn blockierend, was in deinem Fall eigentlich einer Dauerblockade gleichkommt.
Je nach Drehzahlen, um die es geht ( das solltest Du uns mal mitteilen ) wäre dein erster Ansatz schon gar nicht so schlecht. Im Interrrupts misst Du die Zeit seit der letzten Flanke. Wenn diese Zeit zu klein ist (Prellen) ignorierst Du den Interrupt. Ansonsten nimmst Du die Zeit als Umdrehungszeit, und startest eine neue Messung.
Das Einrichten des Interrupts machst Du aber im setup(), und deine Zeitvariable für den Interrupt musst Du als globale Variable und als volatile definieren. Wenn Du im loop() auf die Zeitvariable aus dem IRQ zugreifst, musst Du den IRQ sperren, damit Du keine ungültigen Zwischenwerte liest.

Ich brauche eine Drehzahl von ca. 600 Umdrehungen pro Minute.

Ich verstehe das nicht mit dem Setup und Loop was du da geschrieben hast, wärst du bitte so nett und würdest es mir erklären?

Hi

Bei 600 Umdrehungen die Minute haben wir 10 Umdrehungen die Sekunde - in der Zeit, die der Arduino NICHTS zu tun hat, wachsen Dem graue Haare!!

Bei so schnarchlahmem Zeug braucht Es definitiv keinen Interrupt, keinen atomaren Zugriff und keine volatile Variable.
(die unbekannten Worte lassen sich prima einer Suchmaschine zum Fraß vorwerfen, solltest Du auf Interrupt bestehen)

Mein Weg wäre den Sensor zu pollen, also in jedem loop()-Durchlauf nachzugucken, ob Da gerade der Magnet erkannt wird.
Wenn Das letzte Mal bereits > 20ms her ist, wird diese Erkennung 'genommen' - ist der erste Kontakt und kein Prellen.
Der nächste Kontakt ist in 100ms (10 Umdrehungen die Sekunde) zu erwarten, eigentlich ist Alles unter 100ms Prellen (oder Du fährst zu schnell :wink: ).

So brauchst Du nur eine Variable, in Der Du Dir die Zeit der letzten Erkennung merkst.

if (Sensor==AN){
   if (millis()-merkzeit>prellzeit){
      //neue Umdrehung
      zeitproumdrehung=millis()-letzteerkennung;
      letzteerkennung=millis();
   }
   merkzeit=millis();
}

Fertsch.
Bei //neue Umdrehung berechnest Du die vergangene Zeit/die Geschwindigkeit.
Ausgaben auf's Display nur, wenn sich was geändert hat.
Alles zu kurz/schnell wie möglich.
Lieber eine Aktion in zwei Einzelaktionen aufteilen, als z.B. bei der Ausgabe der Geschwindigkeit ein Flackern zu bekommen (State-Maschine).

MfG

eigentlich ist Alles unter 100ms Prellen (oder Du fährst zu schnell :wink: ).

10 ms Prellen wäre schon viel. Also kannst du mit postmaster's Vorschlag Drehzahlen bis 6000 RPM messen.

Das sollte reichen.

Ohne Interrupt, ohne pulseIn.

Also 600 U/min sind definitv kein Problem mit Pollen. Da brauchts keinen Interrupt.

Bei 6000 U/min würde ich schonmal ein wenig ins Grübeln geraten. Je nachdem wie genau ich das brauche, und was der Arduino sonst noch alles machen soll, könnte da schon die Interrupt-Variante ins Spiel kommen.

Ich muss eigentlich nur einen Tachometer mit Geschwindigkeit, Tageskm und Gesamtkm machen, aber danke für die Hilfe :slight_smile:

Ich habe den obenstehenden Codeteil in meinen Code eingefügt, aber es funktioniert nicht, kann mir wer sagen, warum nicht?