Accelstepper.h und ein Programmierproblem :/

Hallo liebe Community;

ich möchte mittels Accelstepper einen Schritt-Motor betreiben, der über die DIR und STEP-Pins und ein Motor-Shield a4988 angesteuert wird. Soweit sogut - funktioniert bei einem solch einfachen Beispiel auch:

#include <AccelStepper.h>

AccelStepper SLIDER(AccelStepper::DRIVER, 8, 9); //8=Step 9=Dir



void setup() {
   SLIDER.setMaxSpeed(250);
 SLIDER.setSpeed(-250);

}

void loop() {
 SLIDER.runSpeed();

}

Allerdings kommt jetzt das Problem, dass ich in meinem Setup einige Hardware-Tasten angeschlossen habe. Um diese Tasten und deren Analogwerte auszulesen habe ich mir entsprechenden Code geschrieben, der aber anscheinend schon dafür sorgt, dass die Accelstepper Bibliothek nicht mehr flüssig und schnell genug läuft. (ich verwende keine Delays)

Die Geschwindigkeit des Motor ist erheblich langsamer als in dem einfachen "Codesetup", was ich oben gepostet habe.

Egal wie ich es versuche zu programmieren, habe ich das Gefühl, dass ein paar IF-Abragen und analog-Read Befehle den kompletten Programmprozess unglaublich verlangsamen. Von dem LCD-Display was über IC2 angesteuert will ich garnicht erst schreiben.

Ich brauche von euch einen grundlegenden Tipp was ich versuchen kann.

 joy_X_live = analogRead(A3);

if (joy_X_live > 500) { 
  

SLIDER.runSpeed();
}

Alleine diese IF-Abfrage verlangsamt die Geschwindigkeit des Motors um die Hälfte und ich verstehe nicht warum. Wird durch eine IF-Abfrage der Programmzyklus so verlangsamt?

Es muss doch darüber hinaus auch einen Weg geben, mit einem IC2 Display und einem Motor am gleichen Arduino zu arbeiten. Sobald ich auch nur einen Wert im Loop-Teil des Programms auf das Display schre<iben lasse, bewegt sich der Motor nur noch in Zeitlupe.

Klar, weil Du runSpeed() nur manches Mal aufrufst.
Außerdem zeige uns bitte den kompletten, kompilierfähigen Code.

Gruß Tommy

Du könntest es ja mal mit meinen MobaTools probieren. Die erzeugen die Steps in Timerinterrupts und sind damit vom Sketchablauf unabhängig. Sie lassen sich auch über den Bibliotheksmanager installieren.

Tommy56:
Klar, weil Du runSpeed() nur manches Mal aufrufst.
Außerdem zeige uns bitte den kompletten, kompilierfähigen Code.

Gruß Tommy

Ich bin davon ausgegangen, das der Befehl "runSpeed()" nur einmal aufgerufen werden muss und der Motor läuft, bis ich den "stop" Befehl sende.

Gerne poste ich den ganzen Code, aber der ist jetzt schon recht "massiv" :slight_smile:

#include <Wire.h>
#include <LiquidCrystal_I2C.h> 
#include <AccelStepper.h>


LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 

AccelStepper SLIDER(AccelStepper::DRIVER, 8, 9); //8=Step 9=Dir



// Für RotaryEncoder
#define encoderPinA 2
#define encoderPinB 3
 
 int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management
 
// interrupt service routine vars
boolean A_set = false;            
boolean B_set = false;


// Für Taster und Joystickauswertung  
 int Drucktaste_Joystick; //Joystick (NC)
 int joy_X_live = 0; //Livedaten Joystick X Achse
 int joy_Y_live = 0; //Livedaten Joystick Y Achse
 int joy_X_befehl;
 int joy_Y_befehl;

//Taster am Drehgeber
 boolean laststate_Drehgeber=false;        // letzter Speicherwert aus zyklus davor
 boolean actstate_Drehgeber=false;        // actueller Status
 boolean togglestate_Drehgeber=false;    //  zu toggelender Wert

//Taster am Joystick
 boolean laststate_Joystick=false;        // letzter Speicherwert aus zyklus davor
 boolean actstate_Joystick=false;        // actueller Status
 boolean togglestate_Joystick=false;    // zu toggelender Wert



//Entprellung Initialisierung
unsigned long previousMillis = 0;                                                                 
const long interval = 1000;
unsigned long currentMillis = millis();     


 
void setup() { //Grundeinstellungen


pinMode(2, INPUT); // Stellgeber Pin A
pinMode(3, INPUT); // Stellgeber Pin B
pinMode(4, INPUT); // Taster am Stellgeber
pinMode(5, INPUT_PULLUP); // Taster am Joystick
pinMode(6, INPUT); //Initialisieruungstaster
       //7 ist frei


  pinMode(encoderPinA, INPUT); 
  pinMode(encoderPinB, INPUT); 
 
  digitalWrite(encoderPinA, HIGH);  // turn on pullup resistors
  digitalWrite(encoderPinB, HIGH);  // turn on pullup resistors
 
  attachInterrupt(0, doEncoderA, CHANGE); // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, doEncoderB, CHANGE); // encoder pin on interrupt 1 (pin 3)

 SLIDER.setMaxSpeed(25000);
 SLIDER.setSpeed(-25000);

 
  lcd.begin(20,4);
 

 } 
   
void loop() {



  //  lcd.setCursor ( 0, 0 );  
 //   lcd.print("Speed:");  
  //  lcd.setCursor ( 7, 0 ); 
  //  lcd.print(encoderPos);

  //  lcd.setCursor ( 0, 1 );  
  //  lcd.print ("POS.A:" );
  //  lcd.setCursor ( 7, 1 );  
  //  lcd.print(POS_A);

 //   lcd.setCursor ( 0, 2 );  
 //    lcd.print ("POS.B:" );
 //   lcd.setCursor ( 7, 2 );  
 //   lcd.print(POS_B);

// lcd.setCursor ( 13, 1 );
   // lcd.print(digitalRead(6));  

   // lcd.setCursor ( 15, 1 );
   // lcd.print(Initialisierung);  

// fullsteps = slidersteps / 16;



joystickAbfragen(); //Befehl für die Abfrage des Joysticks
taster_Drehgeber_Abfragen ();  //Befehl für die Abfrage der Funktionstaster
taster_Joystick_Abfragen ();
Drehgeber_Abfragen ();





  
if (joy_Y_live <500) { // Y - Joysticküberwachung / Steuerung --- RECHTS
  

SLIDER.runSpeed();
}

if (joy_Y_live >505) { // Y - Joysticküberwachung / Steuerung --- LINKS
  

SLIDER.runSpeed();
}

     

 
void joystickAbfragen() { //Joystickabfrage in der Grundstellung
  
 joy_X_live = analogRead(A3);


 joy_Y_live = analogRead(A2);


}

void taster_Drehgeber_Abfragen () {  //Taster vom Drehgeber abfragen

  // LOOP - FUNKTION aktivieren / deaktivieren  
 actstate_Drehgeber=digitalRead(4);              // Den Aktuellen Wert einlesen
if (actstate_Drehgeber==true&&laststate_Drehgeber==false){           // Wenn der aktueller Zustand HIGH ist und beim letzten durchlauf Low ist ist ein Flankenwechsel
togglestate_Drehgeber=!togglestate_Drehgeber;                             // invertiern des statues
laststate_Drehgeber=true;                                          // Der letzte Zustand des Pines ist high hier läuft er nicht mehr hin ausser man lässt den Taster los
}
if (actstate_Drehgeber==false){                                  // Wenn der Taster losgelasen wurde ist auch der letzte Pinstatus LOW
laststate_Drehgeber=false;
}


if (togglestate_Drehgeber == 1) { 
  lcd.setCursor ( 12, 3 );  
  lcd.print("LOOP OFF");
  }
 else {
  lcd.setCursor ( 12, 3 );  
  lcd.print("LOOP ON ");
 }
}

void taster_Joystick_Abfragen () {
  
  // POS A / B Wert Taster 
 actstate_Joystick=digitalRead(5);                                 // Den Aktuellen Wert einlesen
if (actstate_Joystick==true&&laststate_Joystick==false){           // Wenn der aktueller Zustand HIGH ist und beim letzten durchlauf Low ist ist ein Flankenwechsel
togglestate_Joystick=!togglestate_Joystick;                        // invertiern des statues
laststate_Joystick=true;                                           // Der letzte Zustand des Pines ist high hier läuft er nicht mehr hin ausser man lässt den Taster los
}
if (actstate_Joystick==false){                                     // Wenn der Taster losgelasen wurde ist auch der letzte Pinstatus LOW
laststate_Joystick=false; 
}


if (togglestate_Joystick == 1) { 
  lcd.setCursor ( 6, 1 );  
  lcd.print("\48");
  lcd.setCursor ( 6, 2 );  
  lcd.print(" ");
  POS_A = fullsteps;
  }
 else {
  lcd.setCursor ( 6, 2 );  
  lcd.print("\48");
  lcd.setCursor ( 6, 1 );  
  lcd.print(" ");
  POS_B = fullsteps;
 }
  
  
  
  
  }

void Drehgeber_Abfragen (){

rotating = true;  // reset the debouncer
 
  if (lastReportedPos != encoderPos)
  {
    Serial.print("Index:");
    Serial.println(encoderPos, DEC);
    lastReportedPos = encoderPos;
  }
}
 
// Interrupt on A changing state
void doEncoderA()
{
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;
    rotating = false;  // no more debouncing until loop() hits again
  }
}
 
// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      encoderPos -= 1;
    rotating = false;
  }

  
}

Bin kein Profi. Ich denke mal das wird klar, wenn man den Code sieht.
Meine direkte Frage an euch ist in erster Linie:

Ist das programmverlangsamend, wenn ständig alle Taster und Stellgeber abgefragt werden?
Muss ich das anders lösen damit parallel der Accel-Stepper funktioniert?

Mein finales Funktionsziel soll sein:

Mit dem Joystick kann man den Kameraschlitten manuell hin und her fahren. An den gewünschten Positionen wird die aktuelle Schrittzahl durch drücken der Joysticktaste in Merkern gespeichert.

Ein späterer "GO"-Taster soll dann die Funktion auslösen, dass der Kameraschlitten von der gespeicherten Position A nach Position B fährt. Die Geschwindigkeit des Motors muss auch noch eingestellt werden können.

Ich wollte halt langsam von "unten" anfagen. Daher habe ich jetzt erst einmal alle Hardware angelötet und so programmiert, dass Sie "verarbeitet" werden kann.
Im nächsten Schritt hatte ich alle Werte der Taster und Merker auf dem LCD Display ausgegeben. Hier musste ich aber feststellen, dass dadurch der Programmzyklus massiv verlangsamt wird :frowning:

Vielleicht hat einer einen guten Tipp oder sogar Bock, mir längerfristig bei dem Projekt zu helfen :slight_smile:

Liebe Grüße
Adrian

addy-b:
Ich bin davon ausgegangen, das der Befehl "runSpeed()" nur einmal aufgerufen werden muss und der Motor läuft, bis ich den "stop" Befehl sende.

Du solltest von nichts Willkürlichem ausgehen, sondern die Doku zum Accelstepper lesen:

Poll the motor and step it if a step is due, implementing a constant speed as set by the most recent call to setSpeed(). You must call this as frequently as possible, but at least once per step interval,

Returns
true if the motor was stepped.

Gruß Tommy

Hallo,

ich vermisse

SLIDER.run()

das muss immer im loop aufgerufen werden weil es die Impuls-Signale für den Motor erzeugt.

Mit z.B
stepper.setSpeed(100);
stepper.move(2000);

legst du z.B Speed und Weg fest , das kann dann auch im loop z.B in einem If Konstrukt stehen wenn ein Taster betätigt wurde.

mit
aktualpos = stepper.currentPosition();

kannst Du die aktuelle Position auslesen

Schau dir die Beispiele und die Doku zur Lib an

Heinz

Hallo Adrian,

addy-b:
Ich bin davon ausgegangen, das der Befehl "runSpeed()" nur einmal aufgerufen werden muss und der Motor läuft, bis ich den "stop" Befehl sende.

So ähnlich würde es bei meiner Lib funktionieren.

Die Accelstepper arbeitet aber ganz anders. Die Methoden run() oder runSpeed() erzeugen immer nur maximal einen einzigen Step pro Aufruf. Da musst Du im Sketch dafür sorgen, dass sie häufiger aufgerufen werden, als es der gewünschten Steprate entspricht.

Zu deinem Programm, da sind mit so beim ersten Überfliegen ein paar Dinge aufgefallen:

  • Wenn Du Pinnummern mit #define einen Namen zuordnest, solltest Du den danach durchgängig benutzen. Besser als '#define' ist in dem Fall auch 'const byte' zu benutzen. Aber das ist in dem Fall das kleinere Problem :wink: .
  • In einer Interruptroutine ist delay() verboten. Zum einen müssen Interrupts möglichst kurz sein, zum anderen basiert delay() selbst auf einem Interrupt und funktioniert daher in einem anderen Interrupt nicht zuverlässig.
  • Drücke mal ctrl-T umd deinen Code zu formatieren. Dann ist er wesentlich besser zu lesen.
  • der Code lässt sich auch nicht übersetzen. Zum einen fehlt wohl eine } zum Beenden von loop, und dann gibt es noch undefinierte Variable.

Danke MicroBahner für die ausfühliche Antwort,

ich bin ja wirklich nicht branchenfremd (gelernter Elektroniker) und habe schon oft SPS-Anlagen programmiert, aber diese Arduino-Geschichte macht mich fertig ;D

Es kann sein dass im Code die ein oder andere "alte" Variable drin ist aus verzweifelten Versuchen weiter vorran zu kommen.
Ich hänge seit Wochen irgendwie fest.

Irgendwie fehlt mir noch der AHA!-Effekt. Habe mir jetzt nochmal die komplette Dokumentation von dem Accel-Stepper angesehen. Ich verstehe nicht,wie ich z.B. die Funktion "is Running" als Abfrage in eine IF-Bedingung einbauen kann.

if (SLIDER.isRunning()=true) { sicher nicht. Ich finde die Accel-Dokumentation nicht sonderlich gelungen.

Werde mir mal deiner erwähnten MobaTools Bibliothek versuchen.

Ich hoffe nicht, dass ich am Ende frustriert doch ein sündhaft teures Profi-Equipment kaufe, weil ich es einfach nicht hinbekomme mit dem programmieren.

Liebe Grüße

addy-b:
if (SLIDER.isRunning()=true) { sicher nicht. Ich finde die Accel-Dokumentation nicht sonderlich gelungen.

Dir fehlen einfach die Grundlagen von C/C++. Die musst Du erlernen, um die Beispiele und Hilfestellungen verstehen zu können.

z.B. ist = eine Zuweisung. Was Du tun willst, ist ein Vergleich und der heißt ==

Die Doku von AccelStepper ist eigentlich ganz gut im Vergleich zu anderen Libs, aber ohne Grundkenntnisse (wie auch in allen anderen Bereichen), hilft die beste Doku nichts.

Gruß Tommy

addy-b:
Ich hänge seit Wochen irgendwie fest.

Dann stelle doch mal einen compilierfähigen Code zur Verfügung. Ich habe Deinen geladen, aber die Fehlermeldungen waren einfach zu viele, da breche ich dann ab.

addy-b:
if (SLIDER.isRunning()=true) {

Das ist wie ein weißer Schimmel, es genügt if (SLIDER.isRunning()) {

agmue:
Dann stelle doch mal einen compilierfähigen Code zur Verfügung.

Das wäre auch aus meiner Sicht mal der richtige Ansatz. Das ist ein Code mit bereits soviel Funktionalität und dann aber noch so vielen Fehlern, dass er nicht mal compilierbar, geschweige denn testbar ist.
Fang in kleineren Schritten an, und erst wenn das richtig funktioniert, erweitere um den nächsten Schritt.
Alles auf einmal - das wird nichts.