Einzelner delay in einer sub function ohne delay()?!

Hallo zusammen,

vielleicht denk ich grad im Kreis, aber ich komm nicht drauf:

Ich kenn das Thema delay-Alternative über millis(), aber irgendwie gehen die ganzen Beispiele immer in die Richtung periodische Vorgänge wie ein LED-Blinken etc.

Aber was mache ich wenn ich innerhalb einer Funktion einfach mal einen delay von 100ms benötige ohne den ganzen Ablauf zu blockieren?

Sprich - ich müsste ja im Ablauf der Funktion raus springen, dann in die if (millis() - lasttimedone...) springen etc. und dann wieder zurück an den Aussprungpunkt in der Funktion.

Da fällt mir auf Anhieb nur das thema Interupts ein von früher, aber es soll ja in dem Sinne kein Interrupt passieren, sondern die main loop ja normal weiter laufen.

WAS kann ich also hier tun um einen mydelay(100) zu erstellen, der 100ms Wartezeit bereit stellt ohne die main loop zu stoppen?

Gruß,
M.

Deine Frage zu beantworten ist nicht einfach.
Das hängt sehr vom gesamten Konzept deines Sketches ab.
Bei einfachen Programmen stört ein delay mit 100ms nicht.
Bei komplexen, zeitkritischen Programmen musst du darauf verzichen und es per millis() lösen.

(deleted)

das Prinzip von BlinkWithoutDelay ist aber genau das was suchst.

millis() ist deine Uhrzeit.
zu einem bestimmten Zeitpunkt/Event merkst du dir die Uhrzeit previousMillis.
periodisch prüfst du nun ob deine Wartezeit abgelaufen ist ... millis()-previousMillis>interval

Vorschlag: mach zwei Varianten:

einen minimalen Sketch mit deinem delay(100) so wie du es herkömmlich machen würdest

dann beginne einen Sketch auf basis millis()-previousMillis
Stell beide Sketche ein, dann helfen wir dir beim zweiten.

Das wäre ein typischer Fall für taskDelay() aus den Task Makros von mir oder von Combie :slight_smile:

Ja.

mcgonahy:
Aber was mache ich wenn ich innerhalb einer Funktion einfach mal einen delay von 100ms benötige ohne den ganzen Ablauf zu blockieren?

Sprich - ich müsste ja im Ablauf der Funktion raus springen, dann in die if (millis() - lasttimedone...) springen etc. und dann wieder zurück an den Aussprungpunkt in der Funktion.

Wenn Du aus der Funktion rauswillst, dann reagierst Du doch auf ein Ereignis.
Dieses Ereignis nutzt Du um eine Statusvariable zu setzen und merkst Dir lastmillis.
Ist die Statusvariable gesetzt, überspringst Du den Teil oder brichst mit break ab.
Im loop prüfst Du regelmässig ob mills()-lastmillis>voreingestellteZeit und setzt bei Erfüllung die Statusvariable wieder zurück.
Mach ich mit Tasten gerne:

/*
   Statusvariable und millis() - Sperre einer Taste
*/
const int TasterPin = 6;
boolean state = false;
unsigned long lastmillis;
unsigned long Laufzeit = 1000; // in ms


void setup()
{
  Serial.begin (115200);
  pinMode (TasterPin, INPUT_PULLUP);
}
void loop()
{
  if (!TasterPin && !state)
  {
    lastmillis = millis();
    state = true; // verhindert Neustart vor Ablauf
  }
  if (millis() - lastmillis > Laufzeit)
  // if ((millis() - lastmillis > Laufzeit)&& state) // alternativ mit zusätzlicher Prüfung auf state
    {
    state=false;
  }
}

mcgonahy:
Ich kenn das Thema delay-Alternative über millis(), aber irgendwie gehen die ganzen Beispiele immer in die Richtung periodische Vorgänge wie ein LED-Blinken etc.

Welche für den Arduino nützlichen Vorkenntnisse hast Du?

Ich habe viel mit LED-Blinken gelernt, daher würde ich ein Beispiel von Dir mit delay() begrüßen, um die spezielle Herausforderung erkennen zu können.

Lösungen liegen in den millis(), was bei mir bislang immer funktioniert hat, oder der Verwendung einer Bibliothek oder den von mir noch nicht verwendeten Task Makros (siehe #4 und #5).

Ich hoffe, Du fragst als alter Progrmmierhase nach den Task Makros, denn dann könnte ich was lernen ;D

Das wäre mein Blinkbeispiel:

const byte led_pin = LED_BUILTIN;

void setup()
{
  Serial.begin(115200);
  Serial.println("\nStart");
  pinMode(led_pin, OUTPUT);
}

bool zeitnehmer() {
  const uint32_t intervall = 1000;
  static uint32_t vorhin = 0;
  uint32_t jetzt = millis();
  bool fertig = false;
  if (jetzt - vorhin >= intervall) {
    vorhin = jetzt;
    fertig = true;
  }
  return fertig;
}

void loop() {
  if (zeitnehmer()) {
    Serial.println("interne Zeit: ");
    Serial.println(millis());
    digitalWrite(led_pin, !digitalRead(led_pin));
  }
}

Ich hoffe, Du fragst als alter Progrmmierhase nach den Task Makros, denn dann könnte ich was lernen ;D

Die kann ich dir auch so mal vorführen.....

Im Grunde machen die auch nur das gleiche Verfahren wie du, mit den millis().

Der einzige und ganz wesentliche Unterschied, ist der, dass die millis() Abhandlung nicht mehr offensichtlich im Code sichtbar ist.
Nicht mehr geschrieben werden muss.

Weder die globalen/statischen Variablen, noch der Vergleich.
Das befreit einen von gut 50% Tipparbeit und einer Unmenge an copy&paste Flüchtigkeitsfehlern.

Der eigentlich beabsichtige Kontrollfluss ist deutlicher zu erkennen.

Blink mit TaskMacro

#include <TaskMacro.h>

const byte LED = LED_BUILTIN;

Task Blink()
{
  taskBegin();
  while(1)
  {
    digitalWrite(LED,!digitalRead(LED));
    taskPause(1000);
  }
  taskEnd();
}



void setup() 
{
  pinMode(LED,OUTPUT);  
}

void loop() 
{
  Blink();
}

Die nächste Steigerung wäre dann CooperativeTask.h
Eigentlich die gleiche Suppe, aber dann im OOP Mäntelchen.
Mehr auf Wiederverwendung getrimmt.
Auch dynamisches anlegen von Tasks. usw..

combie:
Die kann ich dir auch so mal vorführen.....

Nun blinkt es mit TaskMacro, danke!

Hinsichtlich Verständnis irgendwann mal in meinem eigenen Thema ::slight_smile:

mcgonahy:
WAS kann ich also hier tun um einen mydelay(100) zu erstellen, der 100ms Wartezeit bereit stellt ohne die main loop zu stoppen?

Gehe weg von der Vorstellung etwas "stoppen" zu müssen.
Der Teil, der diese 100ms Stopp braucht, wird für 100ms bei jeder LOOP Runde "übersprungen".

Sind die 100ms abgelaufen?
ja, dann gehst du wieder in den Programmteil rein.
nein, dann überspringst du ihn, und lässt den loop weiterlaufen.

Das sieht dann so aus:

if (millis() - startzeit >= pausezeit100ms)
{
hier ist der Teil der einmal angefahren und dann 100ms lang übersprungen werden soll
startzeit=millis(); // Und hier startest du nach einem Durchlauf diese Pause wieder neu.
}
hier gehts weiter im loop.....

(deleted)

Hallo,

Die Nachtwächter Erklärung kennst Du ? Eigendlich wird darin ganz gut beschrieben wie man das macht.
Du hast die Verwendung von millis() noch nicht ganz verstanden. Ich versuche es noch mal etwas anders. Grundsätzlich gibt es ja bei delay zwei Ereignisse.

  1. Start der Verzögerung das ist der Moment wenn der Programmablauf auf den delay annkommt.
  2. Ende der Verzögerung ist der Moment wenn die Zeit abgelaufen ist und der Ablauf weiter geht.

wenn Du nun millis verwenden willst musst Du diese beiden Ereignisse ebenfallst verwenden. Du hast sicher schon bemerkt das immer eine Hilfsvariabe für die Zeit mit verwendet wird. Meist wird sie lastime, altzeit oder so ähnlich genannt. Wenn man jetzt die beiden obigen Ereignisse betrachtet ist Start der Moment in dem der Hilfsvariablen "altzeit" der Wert von millis() zugewiesen wird. Das erfolgt meist mittels einer Flankenauswertung eines Signals oder Zustandes.

Anschliessend wird der Wert von altzeit nicht mehr verändert, somit kann man die Differenz zwischen millis und altzeit berechnen und darüber den Zeitpunkt des zweiten Ereignisses Ende bestimmen.

Mal ein ganz einfacher Fall als Beispiel. Nehmen wir mal an wenn ein Schalter eingeschaltete wird soll zeitverzögert eine LED eingeschaltet werden. Dabei kann man auf eine Flanke verzichten und einfach immer dann wenn der Schalter nicht gedrückt ist altzeit=millis() verwenden. Wird er nun gedrückt passiert das nicht mehr und es ergibt sich eine Differenz zwischen millis() und altzeit die man abfragen kann. Ist die Differenz >= dem gewünschten delay ist Ende erreicht.

Ich hab das mal mit einer Function mydelay() realisiert der man das Startereigniss und die gewünschte Zeiverzögerung übergibt. Als Rückgabe erhält man in einer bool Variable 1 wenn die Zeit abgelaufen ist , ansonsten eine 0.

Durch geeignete Änderungen in der Funktion an der Logik kann man daraus leicht Ausschaltverzögerung, Imulszeiten, Zyklische Zeiten, Blinkzeiten machen. Oder Du verwendest eine geeignete LIb dazu.

Mein Beispiel sieht so aus.

/*
 * Funktion mydelay mit millis() als 
 * Einschaltverzögerung
 */

const byte btnpin = 2;
const byte ledpin = 13;
bool btnstate, ledstate; // Hilfsvarable

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(btnpin, INPUT_PULLUP);// Schalter nach GND
  pinMode(ledpin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:

  btnstate = !digitalRead(btnpin);    // Einlesen
  ledstate = mydelay(btnstate, 1000); // Verarbeiten
  digitalWrite(ledpin, ledstate);     // Ausgeben

}
bool mydelay(bool on, uint32_t verzoegerung) {
  uint32_t static altzeit; //Startzeit als static
  
  if (!on) altzeit = millis();// immer gleich
  if (millis() - altzeit >= verzoegerung) { // Zeit abgelaufen
    return (1);
  }
  return (0);
}

Peter-CAD-HST:
ich hab auch noch einen:

Diese Warnung dürfte ein Fehler sein:

[sup]warning: 'repeat' is used uninitialized in this function [-Wuninitialized]
   if (repeat) {
   ^~
[/sup]

Eventuell so:

void TaskName()
{
  static bool  repeat = true;
  // mache dies und das
  // und wenn dass eine Wiederholung benötigt dann
  if (repeat) {
    taskNameRepeatTime = millis() + repeatTime;
    repeat = false;
  }
}

Insgesamt hält sich meine Begeisterung für Deinen Vorschlag in engen Grenzen, da Du globale Variablen nimmst, wo man sie nicht benötigt und zwei Bedingungen hintereinander ist nicht schön zu lesen. Ob millis() + repeatTime den Überlauf übersteht, wäre auch noch zu prüfen.

Oder?

Rentner:
Mein Beispiel sieht so aus.

...

byte mydelay(byte on, uint32_t verzoegerung) {
...

Nur eine Kleinigkeit: Hier würde bool on besser passen.

(deleted)

Peter-CAD-HST:
mein Idee ist ein Vorschlag zur weiteren Bearbeitung an die mir nicht bekannten Gegebenheiten. :slight_smile:

Dann fände ich eine entsprechende Kennzeichnung wie "ungetestet" oder dergleichen hilfreich für den Fragesteller.

(deleted)

agmue:
Nur eine Kleinigkeit: Hier würde bool on besser passen.

Hallo,

völlig richtig , war auch so geplant ich habs geändert.

danke für den Hinweis.

Heinz

Peter-CAD-HST:
ist geändert, Danke für den guten Hinweis 8)

Rentner:
danke für den Hinweis.

Zweimal bitte gerne!

Ich finde es schön, wenn Ihr aktiv am Forum mitarbeitet und hoffe, Ihr habt Spaß dabei!

agmue:
Zweimal bitte gerne!

Ich finde es schön, wenn Ihr aktiv am Forum mitarbeitet und hoffe, Ihr habt Spaß dabei!

Hallo,

ja Spaß hab ich, wäre jetzt nur schön wenn vomT0 eine Rückmeldung käme.

Heinz