Sketch hängt nach einiger Zeit

Hi,

der folgende Code simuliert einen Inkrementalgeber mit AB Spuren an einem Motor. Soweit funktioniert er auch. Der Motor beschleunigt im Uhrzeigersinn, verzögert bis 0 und beschleunigt wieder in die entgegengesetzte Richtung usw… Nach einiger Zeit scheint er sich aber aufzuhängen und die Ausgänge schalten keine Impulse mehr. Könne mir vorstellen, dass es etwas mit dem Micros()-Überlauf zu tun haben könnte, finde aber den Fehler nicht. Da fehlt mir noch etwas die Erfahrung. Wäre für jede Hilfestellung dankbar.

VG

const int a =  2;
const int b = 3;
//const int outDir = 12;

bool dir = true;
unsigned long seq90;
unsigned long seq180;
unsigned long seq270;
float f;
int fMin = 0;
int fMax = 16000;
unsigned long seqLen;
bool runSeq = false;
bool runSeqPrev = false;
unsigned long prevMicros = micros();
unsigned long currMicros;
unsigned long timeDiv;

unsigned long accPrevMillis = millis();
unsigned long accCurrMillis;
unsigned long accTimeDiv;
unsigned long timeBase = 10; //millis
bool accDir = true;
bool fullSpd = false;

void setup() {

//  Serial.begin(250000);
  pinMode(a, OUTPUT);
  pinMode(b, OUTPUT);
  pinMode(outDir, OUTPUT);
  f = fMin;

}

void loop() {

  int timeFactor = 1;

  currMicros = micros();

  timeDiv = currMicros - prevMicros;
  if (!runSeq && (timeDiv >= seqLen)) {
    prevMicros = currMicros;
    runSeq = true;
  }

  if (fullSpd) {
    timeFactor = 500; // =5sec
  }

  accCurrMillis = millis();
  if ((accCurrMillis - accPrevMillis) > (timeBase * timeFactor)) {
    accPrevMillis  = accCurrMillis;
    fullSpd = false;

    if (accDir) {
      if (f < 15) {
        f += 0.05;
      } else if (f < fMax * 0.1 || f > fMax * 0.8) {
        f += 10;
      } else {
        f += 20;
      }
      if (f > fMax) {
        f = fMax;
        accDir = false;
        fullSpd = true;
      }
    } else {
      if (f < 15) {
        f -= 0.05;
      } else if (f < fMax * 0.1 || f > fMax * 0.8) {
        f -= 10;
      } else {
        f -= 20;
      }
      if (f <= fMin) {
        f = fMin;
        accDir = true;
        dir = !dir;
        //digitalWrite(outDir, dir);
      }
    }
    seqLen = (unsigned long)(1000000 / f); // 1/f*nonoSec = 1/f * 1000000

  }

  if (runSeq) {
    if (!runSeqPrev) {
      seq90 = prevMicros + (seqLen / 4);
      seq180 = prevMicros + (seqLen / 2);
      seq270 = prevMicros + (seqLen / 4 * 3);
      runSeqPrev = true;
      
//        Serial.print(f);
//        Serial.print(" ");
//        Serial.println(seqLen);
    }

    if (dir) {
      if (currMicros < seq180) {
        PORTD |= _BV(PD2); //D2 HIGH
      } else {
        PORTD &= ~_BV(PD2); //D2 LOW
      }
      if (currMicros > seq90 && currMicros < seq270) {
        PORTD |= _BV(PD3); //D3 HIGH
      } else {
        PORTD &= ~_BV(PD3); //D3 LOW
      }
    } else {
      if (currMicros < seq180) {
        PORTD |= _BV(PD3); //D3 HIGH
      } else {
        PORTD &= ~_BV(PD3); //D3 LOW
      }
      if (currMicros > seq90 && currMicros < seq270) {
        PORTD |= _BV(PD2); //D2 HIGH
      } else {
        PORTD &= ~_BV(PD2); //D2 LOW
      }
    }
    if (runSeq && currMicros > seq270) {
      PORTD &= ~_BV(PD2); //D2 LOW
      PORTD &= ~_BV(PD3); //D3 LOW
      runSeq = false;
      runSeqPrev = false;

    }
  }
}

Wie wär's mit etwas debug-output auf der Seriellen?

Habs versucht, nach Stunden keine Fehler aufgetreten. Evtl. beeinflusst Serial die Zykluszeit soweit, dass der Fehler nicht auftritt.

if (runSeq) {
    if (!runSeqPrev) {

... und was passiert, wenn runSeq==false? Nix mehr ... und warum? Weil ...

timeDiv = currMicros - prevMicros;
  if (!runSeq && (timeDiv >= seqLen)) {

.. timeDiv nie größer seqLen wird .. ausser du machst Serial.println() rein ... :slight_smile:

sanok12345:
...

Mach' den Code zunächst übersichtlicher und straffe ihn. „Formatiere“ Strukturen (if, while, switch/case ...) so, dass man leicht erkennt, wie der Code „tickt“. Außerdem solltest Du die Flut an Variablen am Anfang so kommentieren, dass klar ist, wofür welche Variable da ist. Evtl. kannst Du auch „sprechendere“ Namen verwenden.

Alles, was den Code leichter verdaulich macht, sorgt dafür, dass mehr Hirn für die Fehlersuche frei ist.

Gruß

Gregor

zwieblum:
… und was passiert, wenn runSeq==false? Nix mehr … und warum? Weil …
… timeDiv nie größer seqLen wird … ausser du machst Serial.println() rein … :slight_smile:

Hi Zwieblum,

timeDiv ist größer, wenn die Sequenz 360° erreicht. Das hat mich aber anscheinend zu dem eigentlichen Problem geführt, also danke.

fMin = 0;

...

if (f <= fMin) {
        f = fMin;
        accDir = true;
        dir = !dir;
        //digitalWrite(outDir, dir);
      }
    }
    seqLen = (unsigned long)(1000000 / f); // 1/f*microSec = 1/f * 1000000

  }

  if (runSeq) {
    if (!runSeqPrev) {
      seq90 = prevMicros + (seqLen / 4);
      seq180 = prevMicros + (seqLen / 2);
      seq270 = prevMicros + (seqLen / 4 * 3);
      runSeqPrev = true;

Das war wohl Division durch 0.

Mit fMin = 1 lief der Code die ganze Nacht durch…
Ich verstehe aber immer noch nicht warum der Fehler erst nach einiger Zeit auftrat. Micros-Überlauf?

gregorss:
Mach’ den Code zunächst übersichtlicher…

Der Vollständigkeit halber hier der kommentierte und hoffentlich übersichtlichere Code:

const int a = 2; //Ausgang Spur A
const int b = 3; //Ausgang Spur B

bool dir = true; //Richtung: 1 = AB, 0 = BA
unsigned long seq90; //Micros() bei 90° der Sequenz
unsigned long seq180; //Micros() bei 180° der Sequenz
unsigned long seq270; //Micros() bei 270° der Sequenz
float f; //aktuelle Frequenz der Sequenz
int fMin = 0.1; // min Frequenz der Sequenz
int fMax = 16000; // max Frequenz der Sequenz
unsigned long seqLen; //Sequenzlänge in MicroSec (Periodendauer für 360°, a=1, b=1, a=0, b=0)
bool runSeq = false; // Sequenz läuft (<270°), dadurch Verkürkzung der Zykluszeit
bool runSeqPrev = false; //Flanke von runSeq

//Timer für Sequenz
unsigned long prevMicros = micros();
unsigned long currMicros;
unsigned long timeDiv;

//Timer für Berechnung der Frequenz
unsigned long accPrevMillis = millis();
unsigned long accCurrMillis;
unsigned long accTimeDiv;

unsigned long timeBase = 10; //millis
int timeFactor = 1; // Zeitfaktor, um fMax zu halten
bool accDir = true; // 1 = beschleunigen, 0 = verzögern
bool fullSpd = false; // fMax erreicht

void setup() {
  pinMode(a, OUTPUT);
  pinMode(b, OUTPUT);
  f = fMin;
}

void loop() {
  
  //Geschwindigkeit für 5 Sek halten, wenn fMax erreicht
  if (fullSpd) {
    timeFactor = 500; // =5sec
  } else {
    timeFactor = 1;
  }

// Frequenz für Beschleunigungs- und Verzögerungskurve berechnen (eine Art S-Kurve)
  accCurrMillis = millis();
  if ((accCurrMillis - accPrevMillis) > (timeBase * timeFactor)) {
    accPrevMillis  = accCurrMillis;
    fullSpd = false;

    if (accDir) {
      if (f < 15) {
        f += 0.05;
      } else if (f < fMax * 0.1 || f > fMax * 0.8) {
        f += 10;
      } else {
        f += 20;
      }
      if (f > fMax) {
        f = fMax;
        accDir = false;
        fullSpd = true;
      }
    } else {
      if (f < 15) {
        f -= 0.05;
      } else if (f < fMax * 0.1 || f > fMax * 0.8) {
        f -= 10;
      } else {
        f -= 20;
      }
      if (f <= fMin) {
        f = fMin;
        accDir = true;
        dir = !dir;
      }
    }
    seqLen = (unsigned long)(1000000 / f); // 1/f*microSec = 1/f * 1000000
  }

  currMicros = micros();

  timeDiv = currMicros - prevMicros;
  if (!runSeq && (timeDiv >= seqLen)) {
    prevMicros = currMicros;
    runSeq = true;
  }

  if (runSeq) {
    if (!runSeqPrev) {
      seq90 = prevMicros + (seqLen / 4);
      seq180 = prevMicros + (seqLen / 2);
      seq270 = prevMicros + (seqLen / 4 * 3);
      runSeqPrev = true;
    }

    //richtungsabhängig Spur A+B schalten
    if (dir) {
      if (currMicros < seq180) {
        PORTD |= _BV(PD2); //D2 HIGH
      } else {
        PORTD &= ~_BV(PD2); //D2 LOW
      }
      if (currMicros > seq90 && currMicros < seq270) {
        PORTD |= _BV(PD3); //D3 HIGH
      } else {
        PORTD &= ~_BV(PD3); //D3 LOW
      }
    } else {
      if (currMicros < seq180) {
        PORTD |= _BV(PD3); //D3 HIGH
      } else {
        PORTD &= ~_BV(PD3); //D3 LOW
      }
      if (currMicros > seq90 && currMicros < seq270) {
        PORTD |= _BV(PD2); //D2 HIGH
      } else {
        PORTD &= ~_BV(PD2); //D2 LOW
      }
    }
    if (runSeq && currMicros > seq270) {
      PORTD &= ~_BV(PD2); //D2 LOW
      PORTD &= ~_BV(PD3); //D3 LOW
      runSeq = false;
      runSeqPrev = false;
    }
  }
}

Falls jemand hier noch Potenzial in Sachen Zykluszeit sieht, wäre ich für Vorschläge dankbar. Die angestrebte fMax = 16000 ist eigentlich nur ca. 14,5kHz. Höhere fMax bewirkt, dass die Spuren nicht mehr sauber ausgegeben werden und die Geschwindigkeitserfassung auf dem 2. Nano fehlerhaft ist.

Danke und Gruß
Alex

Hi

Ich sehe in dem Code-Haufen einen einzigen Kommentar, wo ich mir nicht sicher bin, daß Der auch korrekt ist.
Da ich auch eigene Baustellen habe und dort zu meinem Sketch sogar Kommentare stehen - wer weiß, wann ich Da Mal wieder zu komme, verplemper ich meine Zeit wohl besser Da.

MfG

PS: Soll heißen: Was soll der Sketch Wo machen - und was macht Er statt Dessen?
DA solltest Du anfangen, den Fehler zu suchen.

Hallo,

schau dir mal den Datentyp von f, fMin und fMAx an. Auch die Initialisierung von fMin. Fällt dir daran etwas auf?

Hier haste Glück das die Operator Priorität stimmt

else if (f < fMax * 0.1 || f > fMax * 0.8)

Ich würde aber zumindestens noch eine Klammer für das ODER setzen.
Der Compiler sollte davor auch warnen.

else if ( (f < fMax * 0.1) || (f > fMax * 0.8) )

In der IDE alle Ausgaben einschalten.
Datei > Voreinstellungen >

  • Ausführliche Ausgabe während > beide Haken rein
  • Compilerwarnungen > “ALLE”
    Zeilennummern und Codefaltung sind auch hilfreich.