Hallo, versuche grade einen DC Motor zur steuern mittels PWM. Dies ist mein loop:
Habe eine Lichtschranke, zähle damit die Impulse. Will erreichen, dass der Motor stehen bleibt, wenn die Differenz zweier Werte 0 beträgt. Wenn ich die Lichtschrabke weg ziehe, wird zwar auf dem seriellen Monitor o angezeigt, der Motor dreht sich aber einfach weiter. Wo liegt der Fehler?
[void loop ()
{
analogWrite(3,150);
long x1=x;
delay(1000);
long x2=x;
Serial.println(x2-x1);
if (x2-x1==0)
{analogWrite(3,0);
}
}
void count()
{
x++;
}
Poste mal in den ganzen sketch und benutz die code tags.
Aber so wie dein Code aussieht ist x1 und x2 immer gleich weil während des delays weder x geändert wird noch das Programm überhaupt weiter läuft. Dann setzt du immer den Motor auf 150 egal ob x1-x2=0 ist und erst am ende setzt du den Motor zu 0 dies wird aber wahrscheinlich direkt wieder auf 150 gesetzt da die schleife von neuem beginnt.
Am besten poste den ganzen code
Aber das kann nicht funktionieren.
x wird nie verändert (die Funktion count() wird nie aufgerufen)
als wird x1 immer gleich x2 sein. (Die besser Abfrage ist auch: "if (x1 == X2)"
Aber: du setzt die Ausgangs-PWM auf 0, springst ja aber im loop sofort wieder nach oben und schreibt wieder den PWM-Wert 150.
Ich denke, du meinst eher sowas:
void loop (){
long x1=x;
delay(1000);
// ermittle x mit irgendwelchen Funktionen
long x2=x;
Serial.println(x2-x1);
if (x2 == x1) { analogWrite(3,0);
} else { analogWrite(3,150);
}
}
du solltest wirklich, wie von Uwe und mir vorgeschlagen, die beiden analogWrite in einen if/ else packen, damit nur entweder das eine oder das andere geschrieben wird.
Die Anweisung " long x1=x=0;" geht so ja garnicht!
Wenn du x1 sowieso immer auf 0 setzt, dann spare dir x1 und x2 und frage nur ab auf
"if (x >0)", das reicht völlig.
guntherb:
Die Anweisung " long x1=x=0;" geht so ja garnicht!
Das kompiliert und läuft, ist aber schlechter Stil und macht nicht unbedingt was man will.
Das weist x den Wert 0 zu. Diese Zuweisung gibt dann 0 zurück und weist 0 an x1 zu. Das ist auch der Grund weshalb der beliebte Fehler if(Bedingung = true) kompiliert. Die Zuweisung gibt ihren Wert zurück und dieser wird vom if überprüft.
Jetzt bleibt der Motor gar stehen, solange delay aufgerufen wird. Wie kann man den hier ohne delay auskommen?
void loop (){
analogWrite(3,0);
long x1=x;
delay(1000);
long x2=x;
Serial.println(x2-x1);
if (x2 == x1) { analogWrite(3,0);
} else { analogWrite(3,150);
}}
Habe einige Beispiele für millis() angeschaut, beziehen sich aber alle auf eine LED, da wechselt der Zustand zwischen HIGH und LOW. Aber ich will zwischen 2 Lichtschrnaken werten wechseln. Da ist doch was völlig anderes oder?
Solange du als ersten Befehl in der Loop " analogWrite(3,150);" stehen hast wird der Motor immer laufen.
Warum?
Weil die Loop immer im Kreis läuft, wie der Name schon sagt.
Selbst wenn du also in der IF abfrage den Motor abschaltest, wird wenige µs später wieder der Eingangsbefehl "analogWrite(3,150);" ausgeführt und der Motor läuft weiter. Und die paar µs, die abgeschaltet wird, merkst du nicht.
Probiers doch mal so:
void loop (){
x = 0;
delay(1000);
noInterrupts();
if (x > 0) {
analogWrite(3,150);
Serial.print("Drehzahl erkannt. x = ");
Serial.println(x)
} else {
analogWrite(3,0);
Serial.println(" keine Drehzahl, x =");
Serial.println(x)
}
interrupts();
}
warum? Ich habe die serielle Ausgabe in die IF-Abfrage gelegt, damit du auch siehst, in welchen Zweig das Programm durchläuft.
ich habe x1 und x2 abgeschafft, die brauchts nicht. dafür werden vor der Abfrage die Interrupts abgeschalten, damit sich bis zur Seriellen Ausgabe die Werte nicht ändern.
zum Thema millis() (ich denke du spielst auf das Beispiel Blinkwithoutdelay an?):
In diesem Beispiel wird nur gezeigt, wie du ohne delay eine Aktion nach einer Wartezeit ausführen kannst, ohne mit delay() alles zu blockieren. Ob diese Aktion das schalten einer LED ist, oder das setzen von PWM oder was auch immer ist egal.
das hat aber nichts mit deinem aktuellen Problem zu tun. Das ist der nächste schritt.
Die Schleife arbeitet nicht ganz sauber. Manchmal bleibt der Motor stehen, manchmal dreht er sich weiter und der Serielle Monitor zeigt kleine Werte für x an,zum Beispil x=10, obwohl die Lichtschranke gar nicht dran ist. Woran kann esliegen?
Das könnte natürlich an deiner Schaltung liegen.
Es sieht so aus, als wenn interrupts augelöst werden.
Ist der Eingang dann offen? Welche Lichtschranke?
Wie sieht die Schaltung aus?
Oft hilft ein Pullup oder Pulldown am Eingang (je nach Schaltung)
Pullup kriegst du auch mit:
Gibst du das analoge Signal deiner Lichtschranke direkt auf den Interrupt? Wenn da die Spannung genau um die Schaltschwelle des I/Os schwankt, wird eventuell mehrfach ein Interrupt ausgelöst. Idealerweise gibt man das Signal erst mal auf einen Schwellenschalter mit Hysterese, z.B. 74HC14 oder ein Komparator.
Wenn das so ist, sollte das aber immer der Fall sein, egal welchen Code man genau hat. Da hilft es aber wahrscheinlich auch schon den Arbeitspunkt des Phototransistors über den Widerstand etwas zu verändern.
Erstmal was zu der Schaltung. Habe ein Arduino Uno, oben drauf wird ein Motortreiber von Velleman aufgesteckt. Die Lichtschranke http://www.ia.omron.com/data_pdf/data_sheet/ee-spx-w_dsheet_csm474.pdf kommt an GRD, 5 V und an Pin 2.
Es sieht tatsächlich so aus, als ob der Interrupt mehrmals ausgelöst wird. Für den Tastgrad nehme ich den Wert 150. Bei diesem Wert komme schon auf x= 150 und höher, pro Sekunde. Da kommt eine sehr hohe Drehzahl dabei raus. Damit aber der Motor überhaupt anläüft muss ich Tastgrad auf 120 setzen.
Nun taucht ein anderes Problem auf: Die Lichtschranke geht gar nicht mehr aus. Auch wenn man einen Gegenstand dazwischen schiebt. Vorher hat sie es aber getan. Kann die kaputt sein?
Wenn die nicht am Motor dran ist zeigt der Serielle Monitor x=27000, wenn ich die aber vom Pin 2 abtrenne dann x=0 und der Motor bleibt stehen.
Ist die Lichtschranke nicht mehr in Ordnung oder löst sich das Problem, wenn man einen Komparator oder den Trigger wie schon oben empfohlen benutzt?
Also braun an +5V und blau über einen 10-100k an Masse. Und dann an schwarz blau den Wert abgreifen. Wenn du da ein Poti nimmst kannst du auch die Schaltschwelle beeinflussen, aber es ist halt immer noch ein Analogsignal. Da kannst du mit einem Multimeter messen was passiert.
EDIT:
Es sein denn da ist noch irgendwie mehr Elektronik drin als normal. Aber ein Versuch schadet nichts.
Ich würde wie gesagt erst mal die Spannung messen die da raus kommt. Ideal ist natürlich ein Oszilloskop. Statt einem Multimeter kannst du das Ding auch mal an den ADC hängen und dir die Werte auf Serial ausgeben lassen. Nur als Test. Um damit was zu regeln ist das zu langsam.
Was auch gehen könnte ist wenn du den Pin mit einem Pullup auf High legst und an schwarz abgreifst. Dann sollte der Transistor den einfach auf Low runterziehen wenn er schaltet. Aber das kann genauso schief gehen wenn sich das Signal zu langsam ändert. Wenn da eine Scheibe darüber läuft, wird der Transistor nach und nach verdeckt, bzw. geöffnet. Dadurch ändert sich der Lichteinfall graduell und man hat eine Kurve in der Spannung. Zumindest war es bei mir so wie ich sowas mal gemacht habe.
Einen Wiederstand habe ich nicht drin, habe es mit INPUT_PULLUP probiert. Die spinnt manchmal, jetzt scheint sie wieder zu laufen. Jetzt zeigt der serielle Monitor x=20, wenn die am Motor dran ist und 0, wenn nicht. Das erscheint mir realistisch.
Jetzt ist meine Frage: wie kann ich anders schreiben, dass zwischen x1 und x2 1 Sekunde vergehen soll ohne delay. So was wie
x1
if ( millis_neu-millis_alt=millis_soll)
{x2 }; ?
void loop (){
x1=x; Serial.println(x1); // deklariert als long x1;
if (millis() - previousMillis >= interval) { // Intervall=1000
previousMillis = millis(); // aktuelle Zeit abspeichern
x2=x; Serial.println(x2); } //deklariert als long x2;
//z=(x2-x1)*60; // Drehzahl ermitteln, steht im Setup
Serial.print("z:");
Serial.println(z);
if (z==0) { pwm=0;
} if (z<1500){ analogWrite(3,pwm++); Serial.print ("pwm:"); Serial.println(pwm);
} if (z>2500) { analogWrite(3,pwm--); Serial.print ("pwm:"); Serial.println(pwm);
}
}
void count() // Interruptfunktion
{
x++;
//Serial.println(x);
}
Es hat paar Mal funktioniert, dann aber nicht mehr. Der Motor startet gar nicht, für z wird o angezeigt, es werden also keine Umdrehungen registriert. Mit delay(1000) geht es aber. Woran kann es liegen?