Hi,
ich steh aktuell vor einem Problem, welches mir nicht so wirklich einleuchtet.
Und zwar arbeite ich grade an einer Steuerung für meinen Stepper Motor für ein Ball and Beam System. Dafür gibt gibt es eine globale Variable degree, die im Interrupt abgefragt und je nach gewünschter Drehrichtung inkrementiert oder dekrementiert wird. Der Interrupt sieht wie folgt aus:
ISR(TIMER1_COMPA_vect)
{
#if _stepmode == 0 // fullstep
if (degree < 0)
{
if (stepcounter < 3)
stepcounter++;
else
stepcounter = 0;
cw_fullstep();
}
else if (degree > 0)
{
if (stepcounter > 0)
stepcounter--;
else
stepcounter = 3;
ccw_fullstep();
}
#elif _stepmode == 1 // halfstep
if (degree < 0)
{
if (stepcounter < 7)
stepcounter++;
else
stepcounter = 0;
cw_halfstep();
}
else if (degree > 0)
{
if (stepcounter > 0)
stepcounter--;
else
stepcounter = 7;
ccw_halfstep();
}
#else // fullstep
if (degree < 0)
{
if (stepcounter < 3)
stepcounter++;
else
stepcounter = 0;
cw_fullstep();
}
else if (degree > 0)
{
if (stepcounter > 0)
stepcounter--;
else
stepcounter = 3;
ccw_fullstep();
}
#endif
}
void ccw_halfstep() <-- nur als ein Beispiel für die 4 Modi /
{
halfstep();
degree -= DEG_PER_STEP_HALFSTEP;
if (degree < (DEG_PER_STEP_HALFSTEP / 2))
degree = 0;
#if DEBUG
Serial.print(MAP_DEGREE_LONG_TO_FLOAT(degree), 3); Serial.print("° -> "); Serial.print(degree / DEG_PER_STEP_HALFSTEP); Serial.println(" steps left");
#endif
}
Um die Positionen der Wippe abzufragen inkrementiere bzw dekremtiere ich den Winkel um einen Stepp je nachdem ob Halbstepp oder Vollstepp Modus in einer while Schleife bis die jeweilige Endposition per Taster detektiert wird.
Jetzt zu meinem Problem: Und zwar muss ich in der Schleife, wo degree == 0 abgefragt wird mit einem delay arbeiten, was die RPM Geschwindigkeit beeinflusst arbeiten, da sonst die If Abfrage nur einmal zu Beginn der Weilschleife erfüllt ist. Mir wird hierbei nicht wirklich klar, denn im Interrupt wird degree ja wieder um den gleichen, gegenteiligen Wert reduziert / erhört, sodass degree wieder zu 0 wird. Wenn ich mir das das in der while Schleife über ein Serial.println(degree) ausgeben lasse funktioniert es auch ohne delay. Ergibt für mich vorne und hinten aktuell keinen Sinn. Aber vllt hat ja jemand von euch für mich die Lösung.
Das, was ich da oben als quote drin habe, sieht der präprozessor und baut da draus irgendwas.. Der Compiler bekommt nicht mit, was Du da vor hast, weil er was fertiges vorgesetzt bekommt und nur noch einbindet.
Und nochmal: Der Fehler ist immer an anderer Stelle. Darum zeige einen - notfalls minimalistischen - Code, der vollständig ist, das man darauf irgendwas sinnvolles schreiben kann.
Hmm ist jetzt schwierig ein minimalistisches Beispiel zu schicken, da das ganze mittlerweile sehr komplex ist. Zur Not könnte ich den aktuellen Stand vom Projekt als Zip file hochladen. ball_and_beam.zip (5,8 KB)
Die If Abfragen, die du markiert hast, sollen ja nicht wären der Runtime abgefragt werden sondern vor dem Kompilieren ob _stepmode im Headerfile 0 oder 1 ist, ja nach Modus den ich verwenden möchte. _stepmode wird nur im Headerfile difiniert, sonst nirgens.
Nochmal, das Projekt ist mittlerweile zu komplex, als das ich es hier als Codeschnipsel schicken könnte. Daher ist es auch nicht nur in einem Tab geschrieben und macht das ganze hier rein zu kopieren mehr als kompliziert. Leider!
Wie ich das hier als Code schicke, weiß ich aber danke.
Au shit! Ich hab mal kurz ins .zip geschaut....
Das ist ja tiefstes C und zum großen Teil kannst Du das alles in einen TAB packen.
Aber ich sag Dir, das Deine ISR mit den Präprozessoranweisungen "nicht ideal" ist...
Mal sehen, ob ich zusammenbekomme, was da wie funktionieren soll....
Was soll das bringen? Es ist ne Präprozessor Anweisung, die in Abhängigkeit vom definierten _stepmode in der Header Datei nur eine Sache kompilieren soll. Da das ganze am Ende ein Regelkreis wird, kann ich keine unnötigen if Abfragen gebrauchen und würde den Code nur unnötigen aufblasen.
motor.h
...
#define _stepmode 1 // define the desired stepmode: 0 = fullstep | 1 = halfstep
...
#define CHECK_END_POSITION_LEFT() (PINH & (0b1 << 3)) // check if D06 is HIGH == TRUE -> no end position detected | LOW == FALSE -> end position is detected
#define CHECK_END_POSITION_RIGHT() (PINH & (0b1 << 4)) // check if D07 is HIGH == TRUE -> no end position detected | LOW == FALSE -> end position is detected
die while Schleife soll solange bis der Endlagentaster betätigt wurde durchlaufen werden und wenn degree gleich 0 ist degree um einen Step erhöhen. Das Funktioniert mit dem delayMicroseconds(100); auch alles einwandfrei, nur verfälscht das delay meine definierte RPM Geschwindigkeit und mir ist nicht klar, wieso dort ein delay nötig wird.
Können tu ich das sicherlich, aber ob es die Lesbarkeit erhöht wage ich zu bezweifeln. Da das meine Bachelorarbeit wird, untergliedere ich das von vornherein lieber gleich ordentlich. Wie dem auch sei, vielleicht findest du ja das Problem, wieso in den while Schleifen ein delay zur richtigen if-Abfrage nötig ist. Der Interrupt wird ja prinzipiell bei 30 RPM nur alle 5ms aufgerufen.
Und bitte, nicht mit Bachelorarbeit kommen... Ich hoffe, das nachher nur die Steuerung funktioniern soll und das Modell und nicht der Code verteidigt werden muss.
Die Übersichtlichkeit?
Warum kommentierst Du etwas, was aus dem Code eindeutig hervor geht?
Warum brauchst Du zwei Direktiven für Fullstep?
Und komm jetzt nicht, das mit der PPD der Code schmaler wird.
Wenn _stepmode const ist und zur Compileziet bekannt ist, das sich da nix mehr tut, übernimmt der Compiler das runterkürzen.
Gleichzeitig übernimmt er aber auch eine Prüfung und fügt das so ineinander, das alles aufeinander passt.
Ich versuch mir mal nen Bild hzu machen - bedeutet aber mitnehmen und zu morgen.
Na denne.
Deine Vermutung ist, dass das delayMicroseconds(100) ;
erforderlich ist, damit der Endschalter überhaupt erkannt werden kann.
Ob FULLSTEP oder HALFSTEP spielt keine Rolle.
Dann schreib doch mal einen drei (oder 10)-Zeiler,
der diese Vermutung zeigt oder widerlegt.
Hast du übrigens DEBUG auf true?
Dann wirst du für Serial.print in einer ISR hiermit von mir verhauen. Auch wenn es nicht zum Thema gehört und evtl sogar funktionieren kann.
Jetzt habe ich es aus Verzweiflung trotzdem mal getestet, allerdings mit demselben Ergebnis. Die IF-Bedingung ist scheinbar nie erfüllt ohne delay oder nem Serial.println(DEGREE).
void beam_position_init()
{
long degree_init;
uint8_t rpm_init = 30;
uint16_t position_init_steps = 0;
_stepmode ? set_rpm_halfstep(rpm_init) : set_rpm_fullstep(rpm_init);
DEGREE = 0;
// drive the engine ccw to the left end position -> positiv DEGREE
while (CHECK_END_POSITION_LEFT()) <-- funktioniert nicht
{
degree_init = DEGREE;
if (degree_init == 0)
{
_stepmode ? DEGREE += DEG_PER_STEP_HALFSTEP : DEGREE += DEG_PER_STEP_FULLSTEP;
}
//delayMicroseconds(100);
}
Serial.print("While-Schleife 1 Ende - DEGREE : "); Serial.println(DEGREE);
Serial.print("While-Schleife 1 Ende - degree_init : "); Serial.println(degree_init);
delay(2000); // only for testing
DEGREE = 0;
// drive the engine ccw to the right end position -> negativ DEGREE
//while (CHECK_END_POSITION_RIGHT())
while ((CHECK_END_POSITION_LEFT())) <-- funktioniert
{
degree_init = DEGREE; Serial.println(degree_init);
if (degree_init == 0)
{
_stepmode ? DEGREE -= DEG_PER_STEP_HALFSTEP : DEGREE -= DEG_PER_STEP_FULLSTEP;
position_init_steps++;
}
//delayMicroseconds(100);
}
_stepmode ? DEGREE = (DEG_PER_STEP_HALFSTEP * position_init_steps / 2) : DEGREE = (DEG_PER_STEP_FULLSTEP * position_init_steps / 2);
#if DEBUG
#if _stepmode == 1 // halfstep
float DEGREE_to_zero = MAP_DEGREE_LONG_TO_FLOAT((DEG_PER_STEP_HALFSTEP * position_init_steps / 2));
Serial.print("Measurment: "); Serial.print(position_init_steps); Serial.print(" steps -> "); Serial.print(DEGREE_to_zero); Serial.println("° to 0°");
Serial.flush();
#else // fullstep
float DEGREE_to_zero = MAP_DEGREE_LONG_TO_FLOAT((DEG_PER_STEP_FULLSTEP * position_init_steps / 2));
Serial.print("Measurment: "); Serial.print(position_init_steps); Serial.print(" steps -> "); Serial.print(DEGREE_to_zero); Serial.println("° to 0°");
Serial.flush();
#endif
#endif
while (DEGREE > 0)
{
delay(50);
}
_stepmode ? set_rpm_halfstep(RPM) : set_rpm_fullstep(RPM);
}
In der Kosole erhalte ich dann folgendes:
23:59:50.108 -> Set OCR1A for 10 rpm.
23:59:50.108 -> Halfstep OCR1A: 30302
23:59:50.108 -> Set OCR1A for 30 rpm.
23:59:50.108 -> Halfstep OCR1A: 9999
23:59:50.108 -> ccw isr
23:59:50.108 -> 0.000° -> 0 steps left
00:00:13.312 -> While-Schleife 1 Ende - DEGREE : 0
00:00:13.312 -> While-Schleife 1 Ende - degree_init : 900
Nein, das ist nicht meine Vermutung, denn das wird sauber erkannt. Mein Problem ist, dass DEGREE in der While-Schleife niemals als 0 erkannt wird ohne Delay oder einem Serial.print, obwohl es das aber müsste, da DEGREE bei jedem Interrupt wieder Richtung 0 verändert wird.
Berechtigter Einwand auch wenn ich mir dessen bewusst bin dass ne ISR keine langen Rechenoperationen durchführen soll, aber das ändert leider gar nichts.
Ich befürchte viel mehr irgendwo eine Race Condition zu haben, weshalb die IF-Abfrage nicht sauber läuft.
Nachtrag: selbst mit einem delayMicroseconds(1) laufen beide While-Schleifen sauber durch, wenn ich die globale Variable DEGREE in die lokale degree_init speichere. Wenn ich auf DEGREE prüfe läuft nur der eine Step am Anfang und sonst nichts. Daher gehe ich mal stark von einer Race Condition aus, auch wenn mir nicht klar ist aktuell wieso es mit lokaler Variable nicht auch gänzlich ohne Delay funktioniert. Any Ideas?
void beam_position_init()
{
uint8_t rpm_init = 30;
uint16_t position_init_steps = 0;
long degree_init;
_stepmode ? set_rpm_halfstep(rpm_init) : set_rpm_fullstep(rpm_init);
DEGREE = 0;
// drive the engine ccw to the left end position -> positiv DEGREE
while (CHECK_END_POSITION_LEFT())
{
degree_init = DEGREE;
if (degree_init == 0)
{
_stepmode ? DEGREE += DEG_PER_STEP_HALFSTEP : DEGREE += DEG_PER_STEP_FULLSTEP;
}
delayMicroseconds(1);
}
Serial.print("While-Schleife 1 Ende - DEGREE : "); Serial.println(DEGREE);
Serial.print("While-Schleife 1 Ende - degree_init : "); Serial.println(degree_init);
delay(2000); // only for testing
DEGREE = 0;
// drive the engine cw to the right end position -> negativ DEGREE
//while (CHECK_END_POSITION_RIGHT())
while (CHECK_END_POSITION_LEFT()) // only for testing
{
degree_init = DEGREE;
if (degree_init == 0)
{
_stepmode ? DEGREE -= DEG_PER_STEP_HALFSTEP : DEGREE -= DEG_PER_STEP_FULLSTEP;
position_init_steps++;
}
delayMicroseconds(1);
}
Serial.print("While-Schleife 2 Ende - Degree : "); Serial.println(degree_init);
_stepmode ? DEGREE = (DEG_PER_STEP_HALFSTEP * position_init_steps / 2) : DEGREE = (DEG_PER_STEP_FULLSTEP * position_init_steps / 2);
#if DEBUG
#if _stepmode == 1 // halfstep
float DEGREE_to_zero = MAP_DEGREE_LONG_TO_FLOAT((DEG_PER_STEP_HALFSTEP * position_init_steps / 2));
Serial.print("Measurment: "); Serial.print(position_init_steps); Serial.print(" steps -> "); Serial.print(DEGREE_to_zero); Serial.println("° to 0°");
Serial.flush();
#else // fullstep
float DEGREE_to_zero = MAP_DEGREE_LONG_TO_FLOAT((DEG_PER_STEP_FULLSTEP * position_init_steps / 2));
Serial.print("Measurment: "); Serial.print(position_init_steps); Serial.print(" steps -> "); Serial.print(DEGREE_to_zero); Serial.println("° to 0°");
Serial.flush();
#endif
#endif
while (DEGREE > 0)
{
delay(50);
}
_stepmode ? set_rpm_halfstep(RPM) : set_rpm_fullstep(RPM);
}