[gelöst] While schleife -> abfrage funktioniert nicht ohne delay

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.

void beam_position_init()
{
  degree = 0;
  
  uint8_t rpm_init = 30;
  uint16_t position_init_steps = 0;

  #if _stepmode == 0    // fullstep
    set_rpm_fullstep(rpm_init);
  #elif _stepmode == 1  // halfstep
    set_rpm_halfstep(rpm_init);       
  #else                 // fullstep
    set_rpm_fullstep(rpm_init);  
  #endif  
  
  // drive the engine ccw to the left end position -> positiv degree
  while (CHECK_END_POSITION_LEFT())
  {
    if (degree == 0)  <-- Problemstelle
    {   
      #if _stepmode == 0    // fullstep
        degree += DEG_PER_STEP_FULLSTEP;
      #elif _stepmode == 1  // halfstep
        degree += DEG_PER_STEP_HALFSTEP;       
      #else                 // fullstep
        degree += DEG_PER_STEP_FULLSTEP;  
      #endif
    }

    delayMicroseconds(100); 
  } 
  
  delay(2000); // only for testing

  // drive the engine ccw to the right end position -> negativ degree
  //while (CHECK_END_POSITION_RIGHT())
  while (CHECK_END_POSITION_LEFT()) // only for testing
  {
    if (degree == 0)  <-- Problemstelle
    {   
      #if _stepmode == 0    // fullstep
        degree -= DEG_PER_STEP_FULLSTEP;
      #elif _stepmode == 1  // halfstep
        degree -= DEG_PER_STEP_HALFSTEP;       
      #else                 // fullstep
        degree -= DEG_PER_STEP_FULLSTEP;  
      #endif

      position_init_steps++;
    }
    
    delayMicroseconds(100);
  }

  #if _stepmode == 0    // fullstep
    degree = (DEG_PER_STEP_FULLSTEP * position_init_steps / 2);   
  #elif _stepmode == 1  // halfstep
    degree = (DEG_PER_STEP_HALFSTEP * position_init_steps / 2);       
  #else                 // fullstep
    degree = (DEG_PER_STEP_FULLSTEP * position_init_steps / 2);    
  #endif    

  #if DEBUG
    #if _stepmode == 0    // 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()
    #elif _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)  <-- Problemstelle
  {
    delay(50);
  }
    

  #if _stepmode == 0    // fullstep
    set_rpm_fullstep(RPM);
  #elif _stepmode == 1  // halfstep
    set_rpm_halfstep(RPM);       
  #else                 // fullstep
    set_rpm_fullstep(RPM);  
  #endif  
}

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.

Aus den zwei Fetzen?
Du verlässt sogar die sichere Arbeit des Kompiliers.

Was denkst Du, was Dir da helfen kann?

Was meinst du mit ich verlasse die sichere Arbeitsweise des Kompilers?

DEG_PER_STEP_xxxSTEP ist ein fester Wert der vorher einmalig definiert wurde.

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.

Ich versuchs mal und hoffe Du hast in der IDE alles in einem Tab....

Geh mal auf BEARBEITEN - FÜR FORUM KOPIEREN
Dann kommst wieder her und klickst mit der rechten Maustaste in den Antwortpost und dann auf einfügen.

Alternativ: gesamten Code markieren, mit STRG-C kopieren und hier im Antwortpost im Editor oben auf den Codebutton geklickt.
grafik

Und dann mit STRG-V den Code zwischen die Markierung gepresst.

Hallo,
lass doch mal bei deinem If , if else else Konstrukt das # weg was soll das denn ?

ich hab auch deine while Schleife nicht verstande , entweder macht die nix oder du kommst da nie mehr raus

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.

t = ( 60s*min^-1 / (30 * 1/min^-1 * 400 Steps))

Ich versuch mal was.
Wahllos aus einer Ecke gezogen - davon gibts noch mehr...

#if _stepmode == 0    // fullstep
  set_rpm_fullstep(RPM);
#elif _stepmode == 1  // halfstep
  set_rpm_halfstep(RPM);
#else                 // fullstep
  set_rpm_fullstep(RPM);
#endif

Entspricht:

  _stepmode ? set_rpm_halfstep(RPM) : set_rpm_fullstep(RPM);

Oder?

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. :wink:

Genau. Aber was änder die short expression?

Ach je jetzt hast Du seine schöne bedingte Compilierug gekillt , das ist ihm sicher zu aufgeblasen .
ich vermute dennoch es liegt daran

mach mal ich bin raus

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.

1 Like

Jetzt wo du es erwähnst, gebe ich dir recht, dass eine Prüfung auf 1 zuerst mehr Sinn ergibt und das else if überflüssig macht.

Danke, war mir neu. Wieder etwas gelernt.

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);

}