Schrittmotor steuern und LCD-Display ausgeben

Hallo,

ich habe folgendes Problem:

Ich steuere kontinuierlich einen Schrittmotor mit der AccelSteper-Lib an und möchte jetzt noch zusätzlich einige Daten auf einem LCD-Display ausgeben.
Ohne LCD-Ausgabe–> Schrittmotor läuft ohne Ruckler, einwandfrei
Mit LCD-Ausgabe–> Aussetzer immer dann, wenn das LCD-Display beschrieben wird.
Also habe ich im ersten Versuch das Display nur 10 mal pro Sekunde beschrieben:

  timer=millis();

  if (timer-LCD_timer>=100){
  lcd.setCursor(0, 0);  
  lcd.print(millis());

   LCD_timer=timer;
  }

Jetzt kann man die Unterbrechungen des Schrittmotors deutlich mit 10 Hz wahrnehmen… :frowning:

Als nächsten Versuch habe ich dann einen InterruptTimer benutzt um die stepper.run(); Funktion priorisiert außerhalb der Hauptschleife aufzurufen:

void setup() {

cli();//stop interrupts
//set timer2 interrupt at 8kHz
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  // set compare match register for 8khz increments
  OCR2A = 15;// = (16*10^6) / (8000*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS21 bit for 8 prescaler
  //TCCR2B |= (1 << CS21);  
  TCCR2B |= (1 << CS22) | (1 << CS21);  
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);


sei();//allow interrupts
}

und dann:

ISR(TIMER2_COMPA_vect){
 
 stepper.run();
}

Hier wiederrum ist das Problem, dass ich nicht mehr die Maximalgeschwindigkeit des Schrittmotors erreiche. Bei OCR2A < 15 läuft der Schrittmotor dann wieder annähernd mit seiner Geschwindigkeit, mit der er ohne Display lief. Aber das Display wird dann auch nur noch sporadisch und stark verzögert beschrieben.
Es scheint also so, als könnte ich entweder nur einen verzögerungsfreien Schrittmotorbetrieb ohne Display erreichen. Stößt der Arduino Uno hier einfach an seine Performancegrenzen oder mache ich etwas falsch?

Ob in deinem Sketch Fehler sind, können wir nicht sagen, da du nur Fragmente lieferst und den kompletten Sketch geheim hälst.

Es sollte kein Geheimnis sein, nur etwas übersichtlicher :slight_smile:

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

AccelStepper stepper(AccelStepper::DRIVER, 17, 16);

LiquidCrystal_I2C lcd(0x3F,20,4);
void setup() {

cli();//stop interrupts

  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  // set compare match register for 8khz increments
  OCR2A = 25;// = (16*10^6) / (8000*8) - 1 (must be <256)
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS21 bit for 8 prescaler
  //TCCR2B |= (1 << CS21);  
  TCCR2B |= (1 << CS22) | (1 << CS21);  
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);


sei();//allow interrupts

  
  // put your setup code here, to run once:
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);

lcd.begin();
Serial.begin(9600);
 
  stepper.setMaxSpeed(999999);
  stepper.setAcceleration(999999);
  stepper.moveTo(-500);
}


unsigned long LCD_timer;
unsigned long timer;

void loop() {
 
  timer=millis();

  if (timer-LCD_timer>=100){
  lcd.setCursor(0, 0);  
  lcd.print(millis());

   //Serial.println(LCD_timer);
   LCD_timer=timer;
  }
  


      if (stepper.distanceToGo() == 0)
      stepper.moveTo(-stepper.currentPosition());

//stepper.run();
}

ISR(TIMER2_COMPA_vect){
 
 stepper.run();
}

Hi

Dein Display wird über I²C angesteuert - nicht die schnellste Möglichkeit.
Überlege Dir, wie lange es braucht, bis der Display-Inhalt beim Display ist und wie viel Zeit Du zwischen Deinen einzelnen Schritten hast.

Auch könnte man Mal antesten, wie lange die stepper.run() maximal braucht, bis Sie abgearbeitet wurde, also, wie schnell Du den Timer maximal einstellen kannst, bis Alles zusammen bricht.

Du könntest versuchen, in jedem loop()-Durchlauf nur wenige Zeichen an das Display zu senden, Deine Ausgabe quasi selber Zwischen-Puffern.
Wenn in diesem Puffer was drin ist, werden zwei/drei Zeichen zum Display geschickt und auf den nächsten Durchlauf gewartet.
Ergibt zwar unterm Strich wohl mehr gesendete Daten, aber in deutlich kleineren Happen.

Eine Überlegung wäre, auf ein Display mit SPI umzusteigen, oder den I²C etwas zu beschleunigen, wobei SPI um Längen schneller als I²C ist.

Tommy hatte bei Seinem FRam-Beispiel was verbaut, um von den 100kHz auf zumindest 400kHz zu kommen - ich suche Mal.

MfG

PS:
TWBR = 12; // 400 kHz (maximum)
TWBR = 72; // 100 kHz (default)
Siehe FRAM-am-Arduino
... ok, im Nachbar-Forum, aber bleibt Sein Beispiel.

Das kommt darauf an, ob die Displayanbindung das mit macht.

Andere Variante: Den Stepper an einen 2. Prozessor auslagern, der vom ersten nur die Daten bekommt und über ein Pin meldet, wenn er fertig ist. Das hängt dann aber stark von der konkreten Anwendung ab.

Gruß Tommy

postmaster-ino:
Du könntest versuchen, in jedem loop()-Durchlauf nur wenige Zeichen an das Display zu senden, Deine Ausgabe quasi selber Zwischen-Puffern.
Wenn in diesem Puffer was drin ist, werden zwei/drei Zeichen zum Display geschickt und auf den nächsten Durchlauf gewartet.
Ergibt zwar unterm Strich wohl mehr gesendete Daten, aber in deutlich kleineren Happen.

Dabei wird die Verzögerung (die ich jetzt alle 10ms habe) auf kleinere Intervalle aufgeteilt und in Summe rein rechnerisch wahrscheinlich die gleiche Verzögerung entstehen.

postmaster-ino:
Eine Überlegung wäre, auf ein Display mit SPI umzusteigen, oder den I²C etwas zu beschleunigen, wobei SPI um Längen schneller als I²C ist.

Auch interessant, aber ich würde erst gern versuchen mit diesem Display klarzukommen. Priorität hat aufjedenfall die Motorsteuerung.

Tommy56:
Andere Variante: Den Stepper an einen 2. Prozessor auslagern, der vom ersten nur die Daten bekommt und über ein Pin meldet, wenn er fertig ist. Das hängt dann aber stark von der konkreten Anwendung ab.

Diese Möglichkeit habe ich auch schon in betracht gezogen, oder auf einen schnelleren Controller wie den Due umzusteigen, sollte auch Besserung bringen.

Ich habe jetzt mal einen simplen Gednkengang überprüft um die maximale theoretisch erreichbare Geschwindigkeit zu ermitteln. Ausgehend von der Annahme, das in jedem Schleifendurchgang ein Step zum Schrittmotor ausgegeben wird (ist diese Annahmme korrekt??) Dafür habe ich die Schleifenzeit gemessen mit:

 cycleCount++;
  if (cycleCount>=10000)
  {
    //Serial.print("Cycle Time: ");
    //Serial.print((micros()-cycleTime)/cycleCount);
    //Serial.println(" microseconds");
    acycleTime=(micros()-cycleTime)/cycleCount;
    cycleCount=0;
    cycleTime=micros();
  }

Das Ergebnis ist: 7429µs/Schleife im Durchschnitt. Das macht 110^-6/742910^-6=134Hz.
Da ich den Schrittmotor im 4. Mikroschritt betreibe (800 Impulse / Umdrehung) könnte ich mit 134 Hz folgende Winkelgeschwindigkeit erreichen: 134Hz* (1,8/4)=60,3 °/s. Für eine komplette Umdrehung wärhen also theoretisch: 360°/60,3°/s = 5,97s notwendig. Am realen System habe ich jedoch für eine Umdrehung etwa 0,7s gemessen. Rechne ich falsch?

Hi

Das Aufteilen der Display-Daten in kleinere Happen hat den Vorteil, daß Du wenig Zeit brauchst, um Diese zum Display zu schicken.
Diese Zeit musst Du natürlich auch erst Mal haben - aber um 3 Bytes zu senden wirst Du deutlich weniger Zeit brauchen, als für Cursor setzen, 10 Zeichen überschrieben, Cursor erneut setzen, Zahl ausgeben.
Wenn die Zeit zwischen den Steps nur für ein Zeichen reicht, dann halt nur Eins!
Dafür musst Du aber wissen, was an das Display wirklich gesendet wird - gerade bei I²C wird so der Overhead deutlich mehr (jedes Mal die ID des Empfänger, entfällt bei SPI komplett).
Aber Du hast halt die Chance, den Displaytext aufzuteilen, statt 10 Zeichen (>11 Byte zum Display) halt 10x 1 Byte (>20 Byte, aber in Einzelhappen).

7000µs wären ja schon 7ms (!!!) pro Durchlauf - Das ist echt lahm.
Bei Deiner Berechnung denke ich: Du bist nicht im 4-fach Mikro-Schritt. Die Rechnung sieht schlüssig aus.
Hast Du Mal verifiziert, ob 800 Schritte eine Umdrehung sind?
Sonst kümmer sich 'wer anders' um die Steps - in der loop() mit den Durchlaufzeiten klappt Das, wie Du ja auch anmerkst, nicht wirklich.

Wie schnell bekommst Du den Stepper, wenn Du in der loop() stur jeweils nur 1 Zeichen sendest?
Was, wenn Du Das nur alle 10ms zulässt? (wobei der Zeitverbrauch spätestens Dann da ist und, wenn Du die Zeit nicht hast, als Schrittverlust deutlich werden können)

(Ziffern 0-9 würden sich anbieten, da sieht man, ob sich was ändert)

MfG