Go Down

Topic: delay() vermeiden scheitert (Read 2659 times) previous topic - next topic

Demokrit

Moin in die Runde,

ich lerne gerade C und die Besonderheiten des Loop(). Mein erstes Projekt ist ein Kamera Slider. Kommt der Stepper-getriebene Wagen an einer Position an, so sollen sequentiell bestimmte Dinge geschehen, bevor die Fahrt weiter geht. Diese sind:

  • Ein Delay, um Schwingungen des Schlittens zu beenden
  • Kamera triggern (auslösen an)
  • Kamera Trigger Time abwarten
  • Kamera triggern (auslösen aus)
  • Ein Delay, um die Latenz der Kameraauslösung zu berücksichtigen.


Mit „delay()" (siehe auskommentierter Loop() im Code) überhaupt kein Problem. Ich möchte es aber gleich „richtig" machen, und „delay()"s vermeiden (sämtliche Kamera-Trigger Beispiele, die ich finden konnte, nutzen leider delays). Ich kämpfe jetzt seit 2 Tagen mit diesem (mutmaßlich banalen) Problem. Der Trigger wird jedoch ca 1,5s betätigt, statt 0,5s, und auch das restliche Timing scheint völlig daneben. Ich bin mittlerweile ziemlich ratlos, warum. Hier der Code:

Code: [Select]
const byte triggerPin = 10; // Camera trigger

// Time periods of various delays.
const unsigned long carriageSettle = 500;
const unsigned long triggerOpen = 500;  // trigger needs 2 ms in MF mode, 500 ms in AF
const unsigned long camLatency = 500;

// Variable holding the timer values so far. One for each "Timer"
unsigned long carriageSettleTimer;
unsigned long triggerTimer;
unsigned long latencyTimer;

bool carriageActive = false;
bool triggerActive = false;
bool latencyActive = false;
bool sequenceActive = false;


void setup() {
  pinMode (triggerPin, OUTPUT);
  Serial.begin(9600);
}

void toggleTrigger() {
  if (digitalRead (triggerPin) == LOW)
    digitalWrite (triggerPin, HIGH);
  else
    digitalWrite (triggerPin, LOW);
}  // toggleTrigger

/*
void loop() {  // Mit delay()s klappt alles
  delay(1000); // other code execution
 
  delay(carriageSettle);
  toggleTrigger();
  delay(triggerOpen);
  toggleTrigger();
  delay(camLatency);
 
  delay(1500); // other code execution
}
*/

void loop() {
  delay(1000); // other code execution

 
  // Handling the Carriage settle time
  if(!sequenceActive){
    carriageSettleTimer = millis();
    sequenceActive = true;
    carriageActive = true;
  }
  if(carriageActive && (millis() - carriageSettleTimer >= carriageSettle)) {
    // carriageSettleTimer expired
//    Serial.println("Carriage");
    carriageActive = false;
    toggleTrigger();  // activate Trigger
//    Serial.println("Trigger open");
    triggerTimer = millis();
    triggerActive = true;   
  }
  if(triggerActive && (millis() - triggerTimer >= triggerOpen)){
    toggleTrigger();  // deactivate Trigger
//    Serial.println("Trigger closed");   
    triggerActive = false;
    latencyTimer = millis();
    latencyActive = true;   
  }
  if(latencyActive && (millis() - latencyTimer >= camLatency)){
    // Latency expired
    // Prepare for next cycle
//    Serial.println("Latency");   
    latencyActive = false;
    sequenceActive = false;
  }

  delay(1500); // other code execution
}


Die Sequenz wird durchlaufen, dies jedoch mit völlig falschem Timing. Bin für jeden Hinweis dankbar

uwefed

Hast Du millis() verstanden?
siehe Nachtwächtererklährung hier im Form bzw blinkwitoutdelay- Beispiel in der IDe.

Grüße Uwe

Demokrit

Offensichtlich nicht richtig, sonst hätte ich wohl kein Problem. Die Zeit seit Power-Up. Mein Problem ist, dass ich erst nach Ablauf anderer Dinge eine Zeitvariable aktivieren kann.

Nach 2 Tagen Kampf denke ich allerdings aktuell nur noch durch Watte.

combie

  • Ein Delay, um Schwingungen des Schlittens zu beenden
  • Kamera triggern (auslösen an)
  • Kamera Trigger Time abwarten
  • Kamera triggern (auslösen aus)
  • Ein Delay, um die Latenz der Kameraauslösung zu berücksichtigen.



Das ist eine klassische Ablaufsteuerung/Schrittkette.
Sollte also auch so im Programm niedergelegt werden.
(scheint mir so)

Hier solltest/könntest du Anregungen finden:

Ablaufsteuerung
Meine Standardantwort zu Ablaufsteuerungen:
Quote
Eine Stichworte Sammlung für Google Suchen:
Endlicher Automat,
State Machine,
Multitasking,
Coroutinen,
Ablaufsteuerung,
Schrittkette,
BlinkWithoutDelay, 
--
Blink Without Delay
Der Wachmann
--
Multitasking Macros
Intervall Macro
Säge kein Sägemehl.

SpaghettiCode

Hi,

es ist richtig Delays zu vermeiden, damit der Sketch möglichst schnell durchläuft, und z.Bsp. Tasten abfragen kann.
Aber wenn Du beim Fotografieren genaue, oder zumindest immer gleiche Verschlusszeiten brauchst (10 Bilder mit je 1/320s o.ä.) dann würde ich lieber Delays nehmen.
Falls Dein Sketch etwas länger braucht, also z. Bsp. 50ms, dann wäre das genau Deine maximal mögliche Abweichung von Verschlusszeit zu Verschlusszeit. (1/320s = 3,125ms + 50ms = ~1/20s)

Gruß André

uwefed

Für Zeitabstände verwendet man die Differenz eines gespeicherten millis()Wertes zum aktuellen millis()Wert.
Grüße Uwe

uwefed

@SpaghettiCode
Die Steuerung steuert nicht die Belichtungszeit sondern den Moment des Auslösen des Fotos. Darum ist die Genauigkeit (auf die ms) der Zeiten nicht so wichtig.

Grüße Uwe

SpaghettiCode

@ Uwe,
wenn es nur um den Zeitpunkt geht, hast Du natürlich Recht! Sorry. Ich war in Gedanken schon ein Stück weiter, da fotografieren mein anderes Hobby ist ...

Gruß André

Demokrit

Danke für das große Feedback. Ich tauche jetzt erst mal in die StateMachine ein. Ein heftiger Brocken, aber wahrscheinlich zelführend. Melde mich wieder, sobald es etwas zu vermelden gibt.

Demokrit

Das Konzept der StateMachine gefällt mir, und ich dachte, es verstanden zu haben. Alleine: Es funktioniert nicht. Im Serial Monitor werden zwar alle States durchlaufen, aber eine angeschlossene LED zeigt, dass die Intervalle nicht eingehalten werden. Es läuft viel zu schnell ab.

Der Code:

Code: [Select]
const byte triggerPin = 10; // Camera trigger

//Start time, used to calculate when this Timer has expired
unsigned long dummyTimerStart;
unsigned long carriageSettleTimer;
unsigned long triggerTimer;
unsigned long latencyTimer;
unsigned long dummyTimerEnd;

unsigned long currentMillis;

//Interval/delay when/where this Timer expires
const unsigned long dummyStartInterval = 1000UL;    // 1000ms to simulate other code
const unsigned long carriageSettle = 500UL;         // 500ms
const unsigned long triggerOpen = 1000UL;           // trigger needs 2 ms in MF mode, 500 ms in AF
const unsigned long camLatency = 500UL;
const unsigned long dummyEndInterval = 1000UL;      // 1000ms to simulate other code

enum States
{
 StateDummyStart, StateCarriageSettle, StateTrigger, StateLatency, StateDummyEnd
};

States mState = StateDummyStart;

void setup() {
 pinMode (triggerPin, OUTPUT);
//  Serial.begin(57600);
}

void loop() {
 currentMillis = millis();
 StateMachine();    
}

//                    StateMachine()
//======================================================================
void StateMachine()
{
 switch (mState)
 {
   //******************************************
   case StateDummyStart:
     if (CheckTimer(dummyTimerStart, dummyStartInterval, true)) {
//        Serial.println("StartDummy over");
       mState = StateCarriageSettle;
     }

     break;

   //******************************************
   case StateCarriageSettle:
     if (CheckTimer(carriageSettleTimer, carriageSettle, true)) {
//        Serial.println("Carriage settle over");      
       digitalWrite (triggerPin, HIGH);
//        Serial.println("Open trigger");        
       mState = StateTrigger;
     }

     break;

   //******************************************
   case StateTrigger:
     if (CheckTimer(triggerTimer, triggerOpen, true)) {
       digitalWrite (triggerPin, LOW);
//        Serial.println("Close trigger");        
       mState = StateLatency;
     }

     break;

   //******************************************
   case StateLatency:
     if (CheckTimer(latencyTimer, camLatency, true)) {
//        Serial.println("Latency over");        
       mState = StateDummyEnd;
     }

     break;

   //******************************************
   case StateDummyEnd:
     if (CheckTimer(dummyTimerEnd, dummyEndInterval, true)) {
//        Serial.println("End Dummy over");        
       mState = StateDummyStart; //now switching to State StateCarriageSettle
     }

     break;
 
   //******************************************
   default:
     // default code goes here
     break;

 }

} // END StateMachine

// Timer for actions needed 'after' a period of time.
boolean CheckTimer(unsigned long &lastMillis, const unsigned long Interval, boolean restart) {

 // lastMillis = the time this "timer" was (re)started
 // Interval   = interval/delay needed.
 // restart    = do we restart this timer again and again?

 // has this timer expired?
 if (currentMillis - lastMillis >= Interval) {
 
   //should this timer start again?
   if (restart == true) {
     lastMillis = currentMillis;  //get ready for the next iteration
   }

   //This Timer did expire
   return true;
 }

 return false;

} // END CheckTimer()



Doc_Arduino

#10
Dec 11, 2018, 03:21 pm Last Edit: Dec 11, 2018, 03:24 pm by Doc_Arduino
Hallo,

du musst dir für die Sache lastmillis für jeden Zustand separat merken und vergleichen.
Sonst setzt irgendein gültiger Zustand die gemerkte Zeit auf aktuell für den nächsten ...
Die Parameterübergabe true ist übrigens überflüssig.
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Demokrit

Das überflüssige "true" leuchtet mir ein. Den Hauptsatz jedoch verstehe ich nicht. Die Zähler sind doch separiert, und zum nächsten State geht es erst nach Ablauf des vorigen. Klappt jedoch nicht, wie ich zugeben muss.

Doc_Arduino

#12
Dec 11, 2018, 04:14 pm Last Edit: Dec 11, 2018, 05:37 pm by Doc_Arduino
Hallo,

war vorhin auf dem falschen Pfad. Du musst dir last_ms nicht für jedes einzeln merken sondern man muss es für den nächsten Vergleich nachziehen sprich aktualisieren. Sonst sehen alle anderen die alten last_ms und sind immer gültig und rasseln deswegen durch. Habs mal umgebaut.

Code: [Select]

// https://forum.arduino.cc/index.php?topic=584509.0

const byte triggerPin = 10; // Camera trigger

unsigned long currentMillis;
unsigned long last_ms;

enum t_state {
  START, SETTLE, TRIGGER, LATENCY, END
};
t_state mState = START;

//Interval/delay when/where this Timer expires
const unsigned int dummyStartInterval = 1000;    // 1000ms to simulate other code
const unsigned int carriageSettle = 500;         // 500ms
const unsigned int triggerOpen = 1000;           // trigger needs 2 ms in MF mode, 500 ms in AF
const unsigned int camLatency = 500;
const unsigned int dummyEndInterval = 1000;      // 1000ms to simulate other code



void setup() {
  Serial.begin(9600);
  pinMode (triggerPin, OUTPUT);
}

void loop() {
  currentMillis = millis();
  StateMachine();
}

void StateMachine()
{
  switch (mState) {
    case START:
        if (checkTimer(dummyStartInterval) ) {
          Serial.println(F("SETTLE"));
          last_ms = currentMillis;
          mState = SETTLE;
        }
        break;

    case SETTLE:
        if (checkTimer(carriageSettle) ) {
          digitalWrite (triggerPin, HIGH);
          Serial.println(F("TRIGGER"));
          last_ms = currentMillis;
          mState = TRIGGER;
        }
        break;

    case TRIGGER:
        if (checkTimer(triggerOpen) ) {
          digitalWrite (triggerPin, LOW);
          Serial.println(F("LATENCY"));
          last_ms = currentMillis;
          mState = LATENCY;
        }
        break;

    case LATENCY:
        if (checkTimer(camLatency) ) {
          Serial.println(F("END"));
          last_ms = currentMillis;
          mState = END;
        }
        break;

    case END:
        if (checkTimer(dummyEndInterval) ) {
          Serial.println(F("START"));
          last_ms = currentMillis;
          mState = START;
        }
        break;

    default: Serial.println(F("switch case ungültig")); // sollte nie eintreten
        break;

  }

} // END StateMachine


bool checkTimer(unsigned int interval)
{
  bool state = false;

  if (currentMillis - last_ms >= interval) {
    last_ms = currentMillis;
    state = true;
  }
  return state;
}
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Demokrit

Hammer! Es klappt perfekt. Und das Beste daran - ich verstehe es sogar. Und ich war schon kurz davor, aufzugeben.

1000 Dank für Deine Hilfe

Doc_Arduino

Hallo,

immer wieder schön wenn es auch nachvollzogen werden kann. Das ist das Wichtigste bei der Sache.  :)
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

Go Up