für ein Projekt möchte ich die Drehzahl von zwei Motoren gleichzeitig messen und aufzeichnen. Hierzu verwende ich pro Motor einen IR-Sensor, welcher sein Signal an einen Arduino UNO schickt.
In Sachen Arduino programmieren bin ich noch ein ziemlicher Anfänger, folgenden Code habe ich mir mal zusammengebastelt. Er fuktioniert zwar bisher, jedoch wollte ich einfach mal die Meinung von ein paar Experten (euch) zu dem Code einholen...kann ich da noch was optimieren? (garantiert, die Frage ist nur was ) ist meine Lösung über die Interrupts sinnvoll oder gibts da geeignetere Varianten?
volatile unsigned long period_left = 0;
volatile unsigned long period_right = 0;
unsigned long timer_left = 0;
unsigned long lasttime_left = 0;
unsigned long timer_right = 0;
unsigned long lasttime_right = 0;
long rpm_left = 0;
long rpm_right = 0;
unsigned long timer = 0;
unsigned long lasttime = 0;
long update_interval = 500; // in Millisekunden, bei Änderung auch unten anpassen (die 120)
void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);
attachInterrupt(digitalPinToInterrupt(2), RPM_left, RISING);
attachInterrupt(digitalPinToInterrupt(3), RPM_right, RISING);
}
void loop() {
timer = micros();
if (timer - lasttime > update_interval * 1000) {
rpm_left = 60000000 / period_left;
rpm_right = 60000000 / period_right;
Serial.print(rpm_left);
Serial.print(",");
Serial.println(rpm_right);
lasttime = timer;
}
}
void RPM_left () {
timer_left = micros();
period_left = timer_left - lasttime_left;
lasttime_left = timer_left;
}
void RPM_right() {
timer_right = micros();
period_right = timer_right - lasttime_right;
lasttime_right = timer_right;
}
Hallo,
ich denke das sollte so ok sein, Du solltest Dir allerdings mal das ATOMIC_BLOCK-Makro. ansehen.
Den Einwand von @paulpaulson sehe ich nicht ganz so. Wenn Du eine Auswertung machst ohne das neue Werte vorliegen gelten halt noch die Alten.
Ohne ATOMIC_BLOCK kann Dir der Interupt dazwischen fummeln und dann hast Du Daten bei denen H und L byte nicht stimmen.
Heinz
Und dass nur alle 500 ms die gerade aktuelle Drehzahl (und kein Mittelwert o.ä.) angezeigt wird, kann gewollt sein. Aber was muss passieren, damit die Drehzahl 0 wird ?
Guter Einwand , glatt übersehen. Ich habe mal nachgesehen und das selbst mal so gemacht das ich den Zeitpunkt der letzten Messung mit abgefragt habe und wenn die Differenz > irgendwas war den Messwert auf 0 gesetzt.
Hallo zusammen,
vielen Dank für eure Anmerkungen.
Den Atomic Block schaue ich mir mal an, und das die Drehzhal nicht null werden kann ich mir noch gar nicht aufgefallen, vielen Dank für den Hinweiß, werde ich verbessern. Ich melde mich demnächst nochmal mit dem neuen Code
Hi,
habe den Code jetzt mal aktualisiert mit dem ATOMIC_BLOCK, ebenso kann die Drehzahl jetzt auch 0 werden wenn sich die Welle mit weniger als 2 U/s dreht.
Beim weiterentwickeln bin ich allerdings auf eine weiteres PRoblem gestoßen: Die entsprechenden Drehzahlen würde ich gerne auf einer SD-Karte speichern. So wie ich das jetzt allerdings verstehe, muss der befehl SD.close() am ende des programmes ausgeführt werden um die Daten zu sichern. Da die Messung allerdings mit Einschalten des Arduinos beginnt und erst endet wenn die Stromzufuhr weggenommen wird habe ich (oder kenne nur) keine Möglichkeit den Befehl am Ende der Messung auszuführen. Deswegen öffne und schließe ich die datei auf der SD-Karte jedes mal wenn ich was draufschreiben will....so jedenfalls der Hintergedanke, klappt aber nicht, nichtmal die Datei auf der SD-Karte wird erstellt, geschweige denn Daten abgelegt...
Hier der Code, evtl. findet einer von euch meinen Fehler...
Data ist da schon längst geschlossen.
Und nie wieder geöffnet worden.
Wird in der ISR modifiziert, ist aber nicht volatile, also bekommt das Hauptprogramm die Änderung nicht unbedingt mit. Das Auslesen sollte atomar erfolgen
hallo @combie
danke für den Hinweis...kann ich das atomare auslesen auch weglassen wenn ich die Variable als volatile anlege?
und wo schließe ich Data? und öffne ich das nicht mit SD.open? oder müsste dass data=SD.open heißen?
Danke,
Tobi
Eigentlich dachte ich, dass ich mich recht klar ausgedrückt habe....
Natürlich kannst du das auch weg lassen, wenn du unbedingt deine Probleme behalten, oder diese für später aufsparen, möchtest.
Aber Sinn macht das nicht.
Dann noch:
Wieso ist das "data" Handle überhaupt global?
Warum prüfst du nicht ob die Datei überhaupt geöffnet werden konnte?
Wie gesagt, ich bin in dieser Hinsicht noch ziemlicher Anfänger.
okay, stimmt. aber ich öffne doch die datei wieder im loop, kurz bevor ich drauf schreibe, oder? hier:
data = SD.open("RPM.txt", FILE_WRITE);
data.print(rpm_left);
data.print(",");
data.println(rpm_right);
data.close();
}
und danach schließe ich die datei wieder, damit wenn ich den Arduino vom Strom trenne und dadurch die Messung stoppe die Dateien nicht verloren gehen...
das war im Example von der SD. Library auch so...
stimmt, das könnte ich noch einbauen...danke für den Hinweiß
volatile sagt dem Compiler, dass die Variable sich auch von außen ändern kann. Er muss also vorsichtig beim Optimieren sein. Das ist auch für einzelne Bytes relevant.
Wenn eine Variable aus mehreren Bytes besteht, darf sie sich nicht ändern, während sie gerade bearbeitet wird. Da müssen in normalem Code kurz die Interrupts geschlossen werden, damit keine ISR dazwischen funken kann.
Das sind also zwei verschiedene Probleme. Das zweite tritt nur selten auf, ist daher umso kritischer, weil es lange unentdeckt schlummern kann.
Beispiele sollen einfach sein.
"Guter" Code muss das nicht sein
Manchmal kann es sinnvoll sein das Handle zu "behalten", dann mag global die einfachste Variante sein.
Aber genau das willst du ja nicht, die Datei soll ja nach dem Schreiben sofort geschlossen werden.
Wäre data lokal statt global gewesen, wäre der Fehler mit dem öffnen vermutlich sofort aufgefallen! Und wenn nicht dir, dann dem Kompiler.
File data = SD.open("RPM.txt", FILE_WRITE);
data.print(rpm_left);
data.print(",");
data.println(rpm_right);
data.close();
oh, dann hatte ich wohl den falschen Code kopiert, ich bitte um Verzeihung.
ich passe meinen Code an und melde mich wieder sobald ich den Arduino zur Verfügung habe ob alles funktioniert. Aber vielen Dank schonmal für deine Hilfe!
Gruß
der Code läuft so wie ichs will, vielen Dank euch allen!!! Jetzt fehlt noch die Erweiterung um ein GPS-Modul zur Geschwindigkeitsbestimmung, aber da kümmere ich mich jetzt erstmal selbst drum
hier noch der fertige Code für alle dies interessiert:
Hallo,
Ich würde die ISR-Variablen in nur eine Atomic Block in Variable unladen und dann mit denen weiterarbeiten. So greifst du ja unnötig mehrfach darauf zu und es werden unnötig oft die Interrupts gesperrt.
Heinz
Hallo zusammen,
der Code allein läuft (bisher) einwandfrei. Jetzt zu meinem neuem Problem. Ich will das Ganze um eine Geschwindigkeitsmessung mittels GPS erweitern. Am Code allein habe ich noch nichts geändert, aber alleine wenn ich das GPS-Modul (NEO-6M) an den Arduino schließe und die Drehzahlmessung starten will kommen aufeinmal sehr unrealistische Werte heraus.
Hat jemand von euch zufällig eine Ahnung woran das liegen könnte?
Angeschlossen ist das Modul an den Pins 11 und 12.
hier der Code:
/*
IR1:
Vcc - 5V
GND - GND
AO - D2
IR2:
Vcc - 5V
GND - GND
AO - D3
SD:
CS - D5
SCK - D13
MOSI - D11
MISO - D12
Vcc - 5V
GND - GND
*/
#include <SD.h>
#include <SPI.h>
#include <util/atomic.h>
volatile unsigned long period_left = 0;
volatile unsigned long period_right = 0;
unsigned long p_left = 0;
unsigned long p_right = 0;
unsigned long timer_left = 0;
unsigned long t_left = 0;
volatile unsigned long lasttime_left = 0;
unsigned long timer_right = 0;
unsigned long t_right = 0;
volatile unsigned long lasttime_right = 0;
long rpm_left = 0;
long rpm_right = 0;
unsigned long timer = 0;
unsigned long lasttime = 0;
long update_interval = 500;
void setup() {
SD.begin(5);
pinMode(2, INPUT);
pinMode(3, INPUT);
attachInterrupt(digitalPinToInterrupt(2), RPM_left, RISING); //isr akivieren
attachInterrupt(digitalPinToInterrupt(3), RPM_right, RISING);
}
void loop() {
timer = micros();
if (timer - lasttime > update_interval * 1000) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { //variablen umladen und wieder schnellstmöglich freigeben
p_left = period_left;
p_right = period_right;
t_left = lasttime_left;
t_right = lasttime_right;
}
lasttime = timer;
if (micros() - t_left < update_interval * 1000) { //rpm_left = 0 wenn periodendauer länger als Messinterwall
rpm_left = 60000000 / p_left; //60000000 da mikrosekunden, und umwandlung in Minuten
}
else {
rpm_left = 0;
}
if (micros() - t_right < update_interval * 1000) { //rpm_right = 0 wenn periodendauer länger als Messinterwall
rpm_right = 60000000 / p_right;
}
else {
rpm_right = 0;
}
File data = SD.open("RPM.txt", FILE_WRITE); //Datei öffnen, Schreiben und gleich schließen um Sicherung der Messwerte zu garantieren
data.print(round(timer / 1000000));
data.print(";");
data.print(rpm_left);
data.print(";");
data.println(rpm_right);
data.close();
}
}
void RPM_left () {
timer_left = micros();
period_left = timer_left - lasttime_left;
lasttime_left = timer_left;
}
void RPM_right() {
timer_right = micros();
period_right = timer_right - lasttime_right;
lasttime_right = timer_right;
}