Arduino Drehzahlmesser

OP #4051244
Lesenswert?

Hallo,

Ich möchte die Drehzahl einer Lochscheibe mit dem Arduino Nano 
überwachen, und auf ein LCD ausgeben.

Schaltung mit Lischtschranken und Schmidt Trigger ist aufgebaut und gibt 
ein sauberes Rechteck aus.

Die Scheibe hat 12 Löcher, und eine Drehzahl von knapp 1000 bis max 
20.000 Umdrehungen.

Ich bräuchte sogesehen nur den passenden Code.


Kann mir dabei bitte jemand helfen?
#4051245
Lesenswert?

Nick S. schrieb:
> Hallo,
>
> Ich möchte die Drehzahl einer Lochscheibe mit dem Arduino Nano
> überwachen, und auf ein LCD ausgeben.
>
> Schaltung mit Lischtschranken und Schmidt Trigger ist aufgebaut und gibt
> ein sauberes Rechteck aus.
>
> Die Scheibe hat 12 Löcher, und eine Drehzahl von knapp 1000 bis max
> 20.000 Umdrehungen.
>
> Ich bräuchte sogesehen nur den passenden Code.

Dann würde ich an deiner Stelle anfangen zu programmieren. So gesehen.
OP #4051344
Lesenswert?

Die Schaltung von http://www.mikrocontroller.net/articles/Drehzahlmesser 
ist ganz was anderes.

Ich habe einen Arduino Nano, und brauche den entsprechenden Code dazu.

Umdrehungen beziehen sich auf eine Minute, wenns Anfangs keine 20.000 
upm schafft ist egal, sonst kann ich eine Lochscheibe mit nur einem Loch 
montieren. Wär nur schön wenn ich bis knapp unter 1000U/min eine Anzeige 
auf einem LCD hätte.

Es handelt sich um ein 2x16 Zeilen LCD, das ist in den Arduinobeispielen 
dabei, aber kennen mich damit zuwenig aus, bzw funktioniert kaum ein 
Programm von mir :(


lg
OP #4052231
Lesenswert?

Hallo Dieter, danke für den Link, habs nach langer Fehlersuche 
tatsächlich hinbekommen, Bild im Anhang :)

Aber die Verzögerung ist jenseits von gut und böse, gibts irgenwo ein 
Tut mit dem Capture Register und Compare Match, etc?

In diesem Thread Beitrag "Re: Zündkurve verstellen beim 2Takter" hat 
mir Max.D schon einiges an Anregungen gegeben, aber selber schaff ich 
das leider nicht :(


Lg, Kolbi
Angehängte Dateien:
Gast #4052338
Lesenswert?

Nick S. schrieb:
> Aber die Verzögerung ist jenseits von gut und böse

Ja, da wird mit Absicht verzögert - nicht unbedingt optimal ... wie 
eigentlich der komplette Code, wenn man genauer hinschaut.

Ich habe keine Ahnung, ob micros() in float kommen - aber das einfach 
mal übernommen (bin kein Arduino-Nutzer).

Probier mal

...
1
  //Main Loop To Calculate RPM and Update LCD Display
2
  void loop()
3
  {
4
    int rpm = 0;
5
    int last_rpm = 1;
6
    char buffer[7];
7
  
8
    while(1)
9
    {    
10
      //Update The RPM
11
      if(time > 0)
12
      {
13
        //Alternativ 
14
        rpm = 5000000 / time;
15
        //Mitteln - akt. Wert Faktor 3 und letzter Wert Faktor 1  
16
        rpm = ((rpm + (rpm<<1) + last_rpm)>>2);
17

18
        //ODER - wie bisher
19
        //5 Sample Moving Average To Smooth Out The Data
20
        rpm_array[0] = rpm_array[1];
21
        rpm_array[1] = rpm_array[2];
22
        rpm_array[2] = rpm_array[3];
23
        rpm_array[3] = rpm_array[4];
24
        rpm_array[4] = 60*(1000000/(time*12));    
25
        //Last 5 Average RPM Counts Eqauls....
26
        rpm = (   rpm_array[0] + rpm_array[1] + rpm_array[2] 
27
                + rpm_array[3] + rpm_array[4]                ) / 5;
28
      }
29

30
      if(rpm != last_rpm)
31
      {
32
        //Update The Rpm Count
33
        lcd.setCursor(0, 1);
34
        sprintf(buffer,"%5i",rpm);
35
        lcd.print(buffer);   
36
      }
37
      last_rpm = rpm;
38
    }
39
  }
40

41
  //Capture The IR Break-Beam Interrupt
42
  void fan_interrupt()
43
  {
44
    float time_act;
45
    time_act = (micros);
46
    time = (time_act - time_last); 
47
    time_last = time_act;
48
  }

Glücklich bin ich mit der Lösung nicht - wenn ca. 7000 Umdrehungen pro 
Minute überschritten werden geht es "in die Hose". Aus 
Performance-Gründen würde ich die "Alternative" ausprobieren ...

Wie geschrieben bin ich kein Arduino-Spezialist und würde es wohl auf 
"normaler" AVR-Ebene anders lösen. Hoffentlich schaut Karl-Heinz hier 
nicht hin, sonst bekomme ich Hiebe :-)
Gast #4052416
Lesenswert?

Übrigens: Interessant, was Du da gebaut hast.

Habe mir den 2-Takter-Thread auch mal angeschaut. Da bin ich aber 
mechanisch  deutlich zu weit entfernt ... bin zwar vor Ur-Zeiten mal 
Quickly gefahren (immer eine Vergaser-Füllung weit), aber das ist lange 
her :-)
Gast #4052505
Lesenswert?

Nick S. schrieb:
> ich bekomme die Fehlermeldung: error:
> cannot convert 'long unsigned int()' to 'float' in assignment

Kannst Du bitte mal das KOMPLETTE aktuelle Coding plus ALLE 
(Fehler)-Meldungen hier einstellen? Danke ...

Nur zur Information: Mit der Meldung selbst (ohne Zeilen-Referenz) kann 
niemand etwas anfangen. Die Zeilen-Referenz bringt auch nur etwas, wenn 
man das komplette, zugrunde liegende Coding kennt ...
Gast #4052515
Lesenswert?

Nick S. schrieb:
> ich bekomme die Fehlermeldung: error:
> cannot convert 'long unsigned int()' to 'float' in assignment

Und was steht in der betreffenden Code-Zeile

Dieter Frohnapfel schrieb:
> Ich habe keine Ahnung, ob micros() in float kommen - aber das einfach
> mal übernommen (bin kein Arduino-Nutzer).

Google kennst du?
"arduino micros" hilft in diesem Fall weiter ;-)
http://arduino.cc/en/reference/micros
OP #4052799
Lesenswert?

Leute, Leute, bitte lockerbleiben, Gefühle haben hier nix verloren, nur 
Technik bitte! ^^

Ich wollte mit meinem letzen Post wirklich nicht undankbar klingen oder 
so, danke an alle, die sich bisher die Mühe gemacht haben da was 
weiterzubringen!


Hier die komplette Fehlermeldung:

Drehzahlmesser.ino: In function 'void fan_interrupt()':
Drehzahlmesser.ino:63:14: error: cannot convert 'long unsigned int()' to 
'float' in assignment
Error compiling.

Ganz schlau werde ich daraus nicht, ist der Fehler in Zeile 63, oder wie 
ist das zu verstehen?

Keine Ahnung ob der Schmitt-Trigger nötig ist, werds demnächst ohne 
probieren und berichten.


Lg!
Gast #4052909
Lesenswert?

O.K. - ein paar minus-Bewertungen kann ich noch gebrauchen :-)
Lt. Gurgel soll es also unsigned long sein - daher:

1
  #include <LiquidCrystal.h>
2
  LiquidCrystal lcd(3, 5, 9, 10, 11, 12);
3

4
  volatile unsigned long time = 0;
5
  volatile unsigned long time_last = 0;
6
  volatile int rpm_array[5] = {0,0,0,0,0};
7

8
  void setup()
9
  {
10
    //Digital Pin 2 Set As An Interrupt
11
    attachInterrupt(0, fan_interrupt, FALLING);
12

13
    // set up the LCD's number of columns and rows: 
14
    lcd.begin(16, 2);
15
    // Print a message to the LCD.
16
    lcd.print("Drehzahl:");
17
  }
18

19

20
  //Main Loop To Calculate RPM and Update LCD Display
21
  void loop()
22
  {
23
    int rpm = 0;
24
    int last_rpm = 1;
25
    char buffer[7];
26
  
27
    while(1)
28
    {    
29
      //Update The RPM
30
      if(time > 0)
31
      {
32
        //Alternativ 
33
        rpm = 5000000 / time;
34
        //Mitteln - akt. Wert Faktor 3 und letzter Wert Faktor 1  
35
        rpm = ((rpm + (rpm<<1) + last_rpm)>>2);
36

37
        //ODER - wie bisher
38
        //5 Sample Moving Average To Smooth Out The Data
39
        rpm_array[0] = rpm_array[1];
40
        rpm_array[1] = rpm_array[2];
41
        rpm_array[2] = rpm_array[3];
42
        rpm_array[3] = rpm_array[4];
43
        rpm_array[4] = 60*(1000000/(time*12));    
44
        //Last 5 Average RPM Counts Eqauls....
45
        rpm = (   rpm_array[0] + rpm_array[1] + rpm_array[2] 
46
                + rpm_array[3] + rpm_array[4]                ) / 5;
47

48

49
        time = 0;
50
      }
51

52
      if(rpm != last_rpm)
53
      {
54
        //Update The Rpm Count
55
        lcd.setCursor(0, 1);
56
        sprintf(buffer,"%5i",rpm);
57
        lcd.print(buffer);   
58
      }
59
      last_rpm = rpm;
60
    }
61
  }
62

63
  //Capture The IR Break-Beam Interrupt
64
  void fan_interrupt()
65
  {
66
    unsigned long time_act;
67
    time_act = (micros);
68
    time = (time_act - time_last); 
69
    time_last = time_act;
70
  }

Wieso bei Dir die volatile float-Deklarationen zu Beginn keine 
Fehlermeldung auslösen ist mir allerdings ein Rätsel (es sei denn, Du 
hast die gar nicht übernommen ... aber Deinen Code kann ich ja nicht 
sehen ...)

Habe noch ein "time = 0" eingebaut, damit nicht unsinnig bei jedem 
Durchlauf gerechnet wird.
OP #4054214
Lesenswert?

Es geht auch ohne Schmitt Trigger, wieder ein Bauteil gespart ;)


Da ich die Wertemittelung eigentlich gar nicht brauche, hab ich den Code 
dementsprechend geändert:
1
#include <LiquidCrystal.h>
2
LiquidCrystal lcd(12, 11, 5, 4, 3, 13);
3

4
volatile float Zeit = 0;
5
volatile float Vorherige_Zeit = 0;
6

7
void setup()
8
{
9
  //Digital Pin 2 ist jetzt ein Interrupt und reagiert auf fallende Flanken
10
 attachInterrupt(0, Sensor_Interrupt, FALLING);
11

12
  // 2x16 LCD Display einrichten
13
  lcd.begin(16, 2);
14
  // Drehzahl auf dem LCD Ausgeben
15
  lcd.print("Umdrehungen/Min");
16
}
17

18
//Hauptschleife zum Berechnen der Drehzahl und LCD Displayupdate
19
void loop()
20
{
21
  int upm = 0;
22
  
23
  while(1){    
24

25
     //Displayupdates verzögern
26
    delay(200);
27
  
28
  //Letzte Reihe löschen
29
  lcd.setCursor(0, 1);
30
  lcd.print("                ");   
31
  
32
  //Drehzahl ausgeben
33
  lcd.setCursor(0, 1);
34
  lcd.print(upm);   
35

36
lcd.setCursor(4, 1);
37
lcd.print(time);   
38

39
  //Drehzahlberechnung, *12 für die Anzahl der Impulse (Lochscheibe, Lüfterflügel,...)
40
  if(Zeit > 0)
41
  {
42
        upm = 60*(1000000/(Zeit*12));
43
  }
44
 
45
 }
46
}
47

48
//Interrrupt Capture
49
void Sensor_Interrupt()
50
{
51
   Zeit = (micros() - Vorherige_Zeit); 
52
   Vorherige_Zeit = micros();
53
}


Zum messen hoher Drehzahlen würd ich die Drehzahl einfach auf 10er oder 
100er Stellen runden.


Herzlichen Dank für die Hilfe, Projekt fertig! :)

Lg
OP #4055379
Lesenswert?

Sorry, hab das alte Programm erwischt, so müssts gehn:



1
#include <LiquidCrystal.h>
2
LiquidCrystal lcd(12, 11, 5, 4, 3, 13);
3

4
volatile float Zeit = 0;
5
volatile float Vorherige_Zeit = 0;
6

7
void setup()
8
{
9
  //Digital Pin 2 ist jetzt ein Interrupt und reagiert auf fallende Flanken
10
 attachInterrupt(0, Sensor_Interrupt, FALLING);
11

12
  // 2x16 LCD Display einrichten
13
  lcd.begin(16, 2);
14
  // Drehzahl auf dem LCD Ausgeben
15
  lcd.print("Umdrehungen/Min");
16
}
17

18
//Hauptschleife zum Berechnen der Drehzahl und LCD Displayupdate
19
void loop()
20
{
21
  int upm = 0;
22
  
23
  while(1){    
24

25
     //Displayupdates verzögern
26
    delay(200);
27
  
28
  //Letzte Reihe löschen
29
  lcd.setCursor(0, 1);
30
  lcd.print("                ");   
31
  
32
  //Drehzahl ausgeben
33
  lcd.setCursor(0, 1);
34
  lcd.print(upm);   
35

36

37
  //Drehzahlberechnung, *12 für die Anzahl der Impulse (Lochscheibe, Lüfterflügel,...)
38
  if(Zeit > 0)
39
  {
40
        upm = 60*(1000000/(Zeit*12));
41
  }
42
 
43
 }
44
}
45

46
//Interrrupt Capture
47
void Sensor_Interrupt()
48
{
49
   Zeit = (micros() - Vorherige_Zeit); 
50
   Vorherige_Zeit = micros();
51
}
#4055415
Lesenswert?

2 Dinge

In loop() willst du die Endlosschleife
1
  while(1)
nicht haben.


Das Arduino Framework ist so aufgebaut, dass es dir ein main() vorgibt, 
welches vom Prinzip her so aussieht
1
int main()
2
{
3
  Arduino Initialisierungen
4

5
  setup();
6

7
  sei();
8

9
  while( 1 ) {
10

11
    loop();
12

13
    Ardiuno Framework interne Buchhaltung
14
  }
15
}

Die obligate Endlosschleife steckt bereits in diesem vorgegebenem main() 
drinnen, welches du nicht selbst schreibst. D.h. die Funktion loop() 
wird laufend ohne dein Zutun aufgerufen und das willst du auch so haben, 
damit das Framework die Chance kriegt ihre eigene Buchhaltung zu 
erledigen.

Dein
1
void loop()
2
{
3
  ...
4
  while(1) {    
5
    ...
6
  }
7
}

ist daher kontraproduktiv. Denn genau letzters, dem Framework die 
Möglichkeit zu geben ihre eigenen Dinge zu erledigen, unterbindest du 
damit.


Das andere ist der delay, den du da drinnen hast. Du willst keinen delay 
haben (ausser ganz kurze)! Denn in dieser Zeit kann der µC nichts 
anderes machen als Däumchen drehen und noch ISR abarbeiten. Das ist dir 
aber zu wenig.
Zeitsteuerungen im Arduino Framework funktionieren so (am Beispiel mit 
Millisekunden
1
unsigned long lastDone;
2

3
void setup()
4
{
5
  lastDone = millis();
6
}
7

8
void loop()
9
{
10
  unsigned long now = millis();
11

12
  if( now - lastDone > duration ) {
13
    lastDone = now;
14

15
    ... mache, was auch immer du im Abstand 'duration' gemacht haben willst
16
  }
17
}

Auf die Art vermeidest du das unproduktive Däumchen drehen. Das Prinzip 
ist simpel: Wenn du alle 5 Sekunden eine Aktion machen willst und deine 
Uhr hat bei der letzten Aktion den Sekundenzeiger auf 20 gehabt, dann 
ist die nächste Aktion fällig, wenn der Sekundenzeiger auf 25 steht.

Was anderes hast du im Prinzip hier
1
void Sensor_Interrupt()
2
{
3
   Zeit = (micros() - Vorherige_Zeit); 
4
   Vorherige_Zeit = micros();
5
}
auch nicht gemacht. Nur dass natürlich die Verwendung von float an 
dieser Stelle den programmtechnischen Supergau darstellt und du nicht 2 
mal micros() aufrufen willst, weil du ja keine Gewähr hast, dass sich 
der Wert in der Zwischenzeit nicht geändert hat (mal abgesehen vom 
unnötigen Zeitbedarf für den Aufruf)
#4056083
Lesenswert?

Nein. Sorry.
Es ist dein Projekt und du lernst gefälligst programmieren.

Ausserdem denke ich, dass ich mehr als ausführlich die Motivation und 
die Hintergünde geschildert habe. Einfach Copy&Paste und gut ist spielt 
es bei mir nicht mehr. Nicht auf diesem simplen Schwierigkeitsgrad.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren