Go Down

Topic: Arduino Drehzahlmesser, Hallsensor + 1.2" 7-segment Backpack Anzeige  (Read 242 times) previous topic - next topic

firewood2013

Hallo,

ich benötige einen Sketch für einen Drehzahlmesser in Verbindung mit einer 1.2" 7-segment Backpack Anzeige von EXP-Tech und einem Hallsensor.

Ich habe folgenden Sketch, aber die Drehzahl wird nicht korrekt angezeigt /zu hoch), und die Werte springen sehr stark:

Ich habe sämtliche Sketches ausprobiert, aber es funktioniert nicvht.


Code: [Select]

#include <Wire.h> // Enable this line if using Arduino Uno, Mega, etc.
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"

Adafruit_7segment matrix = Adafruit_7segment();
unsigned int rpm;
volatile byte rpmcount;
unsigned long timeold;

void rpm_fun()
{
  //Each rotation, this interrupt
  //function is run twice, so take
  //that into consideration for
  //calculating RPM
  //Update count
     rpmcount++;
   
}

void setup()
{
  matrix.begin(0x70);

  //Interrupt 0 is digital pin 2, so that is where
  //the IR detector is connected
  //Triggers on FALLING (change from HIGH to LOW)
  attachInterrupt(0, rpm_fun, RISING);
 
  rpmcount = 0;
  rpm = 0;
  timeold = 0;

}

//The loop function, as the name implies, is the main processing loop that "runs forever" while the board is powered up. The first statement delays for one second (1000 milliseconds), but note that the interrupt function will break in every time the value of pin 2 changes and run the rpm_fun function. After the 1 second delay, the interrupt is temporarily disabled (this may not be necessary, but seems safer) then the RPM is calculated based on the number of interrupts and the elapsed time between now and the last time the calculation occurred. The result is sent back to the computer over the serial port, then the interrupt is restored.
void loop()
{
     //Update RPM every second
  delay(1000);
  //Don&apos;t process interrupts during calculations
  detachInterrupt(0);

rpm = 30*1000/(millis() - timeold)*rpmcount;
  timeold = millis();
  rpmcount = 0;
 
  //Write it out to Display
  matrix.print(rpm,DEC);
  matrix.writeDisplay();
  //Restart the interrupt processing
  attachInterrupt(0, rpm_fun, RISING);

 }

Tommy56

Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [*code] davor und [*/code] dahinter ohne *).
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

postmaster-ino

Hi

DU wartest eine Sekunde, um dann mit dem BYTE-Wert zu rechnen.
Das klappt nur, wenn in dieser Sekunde der Interrupt nicht öfter als 255x ausgelöst wird.
Ich vermute hier einen Überlauf, dadurch auch die zappelnden Werte.

rmpcount musst Du 'größer' machen, uint16_t (unsigned int) sollte schon passen.
Weiter MUSST Du diese Variable dann atomar auslesen.
Bei Byte ist Das nicht zwingend erforderlich, hier würde ich Das aber trotzdem machen, da Du in der einen Zeile mit dem Wert rechnest und erst zwei Zeilen drunter den Wert zurück auf Null setzt - dazwischen passt locker ein ISR-Aufruf und Dir geht mindestens ein Count verloren.

MfG
anscheinend ist Es nicht erwünscht, einen Foren-internen Link als 'Homepage' einzubinden, damit JEDER nur einen Klick von combie's Liste zum Thema State-Maschine entfernt ist.
... dann eben nicht ...

firewood2013

Hi, danke für die Infos. Könntest Du mir den Code entsprechend ergänzen? Ich bin da Anfänger, aber den Drehzahlmesser bräuchte ich dringend.

postmaster-ino

Hi

Wenn Du mir schreibst, was Du bereits über den Kram, Den ich erwähnte, rausgefunden hast - warum nicht?
Allerdings ist Das hier kein 'schreib mir einen SKetch für mein Problem'-Forum, sondern Hilfe zur Selbsthilfe.

In der ISR brauchst Du keine atomaren Zugriffe - zumindest der AVR kann nur einen Interrupt zur gleichen Zeit.
- die Variable Deines Zähler in uint16_t statt byte ändern (also den Typ, ergibt dann 16Bit Breite)
- vor dem Auslesen der Variable in Deiner loop() die Interrupts sperren, also
noInterrupts();
Dann die Variable auslesen, die Variable Null setzen.
interrupts();
- durch das interrupts(); erlaubst Du die Interrupts wieder und wir haben in kürzester Zeit unseren Wert sauber gelesen.
DANACH erst mit dem Wert arbeiten - Zeit ist Geld - und in der Zeit, wo die Interrupts verboten sind, kann ein Impuls kommen.
Das ist aber noch kein Problem, sobald die Interrupts wieder zugelassen sind, wird Dieser 'nachgeholt' - aber blöd, wenn in der Zeit zwei Interrupts kämen - dann geht Einer verloren!

Eigentlich ist Das bereits die Umschrift Deines Sketch - halt in Worten zur Selbsthilfe statt als Code zum kopieren - Nachteil: man lernt selber was dabei ... und ja, man braucht ein/zwei Minuten zum Umschreiben ;)


MfG
anscheinend ist Es nicht erwünscht, einen Foren-internen Link als 'Homepage' einzubinden, damit JEDER nur einen Klick von combie's Liste zum Thema State-Maschine entfernt ist.
... dann eben nicht ...

firewood2013

Habs mal geänder. War das so gemeint?

Code: [Select]

 void loop()
 {
   
   delay(900);
   
   detachInterrupt(0);

   noInterrupts();

   rpm = 30*1000/(millis() - timeold)*rpmcount;

   interrupts();

   rpmcount = 0;

   timeold = millis();
   
   
   //Write it out to Display

   matrix.print(rpm,DEC);
   matrix.writeDisplay();
 
   attachInterrupt(0, rpm_fun, RISING);


michael_x

rpmcount = 0; muss natürlich bei gesperrtem Interrupt passieren.


detachInterrupt /  attachInterrupt würde ich rausnehmen. Dadurch geht nur unnötigerweise was verloren.

Ob das aber dein "Springen" verursacht ?
Vermutlich war eher der Datentyp byte zu klein für eine ganze Sekunde Impulse.

postmaster-ino

Hi
Code: [Select]

...
volatile uint16_t rpmcount;   //Hier die Variable 'dicker' machen
...
 void loop()
 {
   delay(900);  //würde ich eher mit millis() machen, der Sketch wird wohl noch Mal mehr zu tun bekommen, als jetzt
   //detachInterrupt(0); //den Interrupt NICHT raus nehmen!!
   noInterrupts();  //ein Sperren der Interrupts reicht hier völlig
uint16_t temp=rpmcount;  //NUR die Variable auslesen
rpmcount=0;   //und Nullsetzen
   interrupts();  //Interrupts wieder zulassen, so wenig Zeit wie möglich die Interrupts sperren
   rpm = 30*1000/(millis() - timeold)*temp;  //hier hast Du 'alle Zeit der Welt'
   timeold = millis();
   
   //Write it out to Display
   matrix.print(rpm,DEC);
   matrix.writeDisplay();
   //attachInterrupt(0, rpm_fun, RISING);  //was nicht entfernt wurde, muß auch nicht erneuert werden

Dachte eher so.
Die Zeit, Die die Interrupts gesperrt sind, so kurz wie möglich halten.
Das Rechnen braucht WESENTLICH mehr Zeit, als das Auslesen.
Und dabei darf auch eine ISR zuschlagen - z.B. braucht millis() selber Interrupts, damit die Millisekunden gezählt werden können.
Hier kann Dir ebenfalls schnell Mal 'was durch die Lappen gehen'.

ISRs: so kurz/schnell wie möglich
ISRs sperren: ebenfalls so kurz wie möglich

MfG
anscheinend ist Es nicht erwünscht, einen Foren-internen Link als 'Homepage' einzubinden, damit JEDER nur einen Klick von combie's Liste zum Thema State-Maschine entfernt ist.
... dann eben nicht ...

firewood2013

Hallo,

super...Großen Dank an Deine Hilfe! Jetzt wird mir das Ganze klarer. Aus diesen Interrupts bin ich nie richtig schlau geworden. Auf die Lösung mit der "Zwischenvariablen" temp wär ich auch nicht gekommen.
Wo kann ich nachlesen, wie man delay durch millis ersetzt?

Tommy56

Schau Dir BlinkWithoutDelay an und verstehe es. Dabei kann Dir die Nachtwächtererklärung helfen.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

Go Up