PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : __ Zeitschleife ohne Programmunterbrechung (Aller NeuAnfang ist schwer)



frabe
15.05.2024, 09:09
Hallo zusammen.

Nach 3 Jahren Programmierabstinenz beschäftige ich mich wieder der Arduino-Programierung. Mein Neueinstieg ist doch "zäher" als gedacht...

Folgend möchte ich einen kurzen Summerton (Tasten-Rückmeldung) als Funktion programmieren, ohne das der Programmfluss unterbrochen wird.
Die Funktion wird vom beliebigen Prg.ort aufgerufen und läuft 500ms.
Folgender code funktioniert nicht richtig;




void loop() {
LEDumschalt();
SummerKurzON();
delay(1000); //Prog.ablauf unterbrochen [ms]
}

void SummerKurzON() { //Tasten-Quitierungston
digitalWrite(Summer, 1); //Summer einschalten
static unsigned long StartZeit = millis();

__TESTmarker__(millis()); //Anzeige nach erstem Durchlauf= 2451 // 2.Durchlauf= 3451
__TESTmarker__(StartZeit); //Anzeige nach erstem Durchlauf= 0 // 2.Durchlauf= 2451

if (millis() >= StartZeit + SummerZeitKurz) { //nach erreichen der Zeit, 500ms, Summer ausschalten
digitalWrite(Summer, 0);
StartZeit = millis();
__TESTmarker__(StartZeit); //Anzeige nach erstem Durchlauf= 2451 //2.Durchlauf= 3451
}
}

+ Warum speichert die Var. StartZeit die falsche Zeit "0"?
+ Läuft millis() ab Programmstart oder erstem Funktionsaufruf?
+ Hat das mit dem static-Befehl zu tun?
+ Was mache ich hier grundlegend falsch?

jcrypter
15.05.2024, 17:00
Hi,

ohne das Programm jetzt testen zu können:
Du setzte Startzeit bei jedem Aufruf der Funktion SummerKurzOn neu.
Dann kommen die Testmarker Aufrufe von denen ich nicht weiß was sie machen und wie lange die Ausführung dauert.
Wenn sie aber kombiniert nicht über 500ms dauern wird eine if Bedingung nie true werden. Ich vermute der Summer ist dauerhaft an?
Dann wird dein delay in loop ausgeführt.

Wenn du in der Funktion keinen delay verwenden willst jedoch alle 500ms den Summer ein/aus schalten möchtest solltest Du den delay in der Loop überdenken. Dieser dauert ja schon eine Sekunde, somit bleibt dein Summer immer diese Zeit an, wenn er nicht vor Erreichen des delay abgeschaltet wurde, was er nicht wird da ja kein delay verwendet werden soll usw usf.

Ich nehme an, deine Funktion LEDumschalt soll mit der alle eine Sekunde aufgerufen werden?
ich bin mir nicht ganz sicher was DU erreichen willst.
Ich versuche mal etwas zu schreiben ohne es zu testen, und es ist eher Pseudocode, habe auch lange keinen Arduino mehr programmiert:




unsigned long startzeit_led = 0;
unsigned long startzeit_summer = 0;
unsinged long dauer_led = 1000;
unsigned long dauer_summer = 500;
bool summer_an = false;

loop(){

if (startzeit_led + dauer_led<= millis()){
LEDumschalt();
startzeit_led = millis();
}

if (startzeit_summer + dauer_summer <= millis()){
if (summer_an)
summer_aus();
else summer_an();
startzeit_summer = millis();
}

}

gegebenenfalls musst Du die Bedingungen erweitern, jenachdem was beim ersten Durchlauf mit startzeit = 0 passieren soll, sonst startet der Summer vor den LEDs.
Die variable summer_an muss entsprechend in den Funktionen die den Summer an/aus machen gesetzt werden.

Ich hab nochmal kurz gegoogled: diese Seite erläutert es gut.
https://starthardware.org/timer-mit-arduino-alternative-zu-delays/

Crypi

frabe
15.05.2024, 18:57
Hi Crypi .
Vieln Dank für deine Mühe :-)

Mein oben genannter Code ist ledigtlich eine Testumgebung, um wieder in die Programmier-Feeling zu kommen.
Tatsächlich klappt das mit den LED-Umschaltung, der Summer springt dann für ~1ms an.

Die TestMarker dienen ledigtlich der Visualisierung am Monitor wie die Variablen gerade aus sehen.

Hierbei sollen alle 1Sek 2 LEDs togglen. Bei jeder Umschaltung soll ein Ton (SummerKurzON()) erfolgen.
Später wird diese Unschaltroutine jedesmal bei Tasteneingabe erfolgen - also ohne 1Sek-Interval.
Alles ohne den loop()-Verlauf zu unterbrechen - auch nicht durch ein delay() in einer Funktion.

2 grundlegende Sachen ich nicht verstanden;
+ Was passiert mit einer static-Variable in einer Funktion, wenn er diese Funktion verlässt?
+ Wann genau wird millis() gestartet? Erst wenn millis() das erst mal in irgend einer Funktion abgefragt wird - Bsp: StartZeit=millis()?

Und in meinem Bsp-Code;
+ Warum ist beim aller ersten Aufruf folgender Funktion


void SummerKurzON() {
static unsigned long StartZeit = millis();

__TESTmarker__(millis());
__TESTmarker__(StartZeit);

StartZeit-Inhalt nicht 100% gleich wie millis() ?
millis() wird hier auch das erste mal verwendet.

jcrypter
16.05.2024, 11:50
Hi,

https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/static/
sie ist nur in der Funktion sichtbar bleibt aber über mehrere Funktionsaufrufe erhalten.

https://www.arduino.cc/reference/de/language/functions/time/millis/
seit Beginn des Sketches, hier ist auch ein Beispielprogramm das du ja mal ausprobieren kannst genannt.
Millis() läuft direkt ab Start, StartZeit wird später im Programm gesetzt, daher die Abweichung.

Crypi

frabe
16.05.2024, 14:30
DANKE für deine Klarheit Crypi



Millis() läuft direkt ab Prg.Start, "StartZeit" wird später im Programm gesetzt, daher die Abweichung.

Das millis() direkt ab Prg.Start läuft, habe ich verstanden!
Warum aber "StartZeit" eine "0" ausgibt obwohl 1 Zeile darüber "StartZeit" die akt. millis() einliest (=2451ms) ist mir immer noch schleierhaft.

- - - Aktualisiert - - -

Ich habe "static" einmal gelöscht, seitdem ist millis() und "StartZeit" gleich.
Sieht so aus, als wenn die static-Variable bei Programmstart die millis() einliest...

Rabenauge
17.05.2024, 09:28
Wenn du zeitgesteuerte Abläufe haben willst, ohne den Controller durch delay()'s zu blockieren, schau dir einfach mal das mitgelieferte Beispiel "BlinkWithoutDelay" ganz genau an.
Wenn man das ein bisschen umstrickt, kann man damit prima Timer bauen, die ohne jegliches delay() funktionieren.
Ich packe da regelmässig nen Sekundentick (oder auch kürzer, je nach Intervall, dass ich benötige) rein, den ich dann für alles Mögliche benutzen kann. Damit kann man problemlos auch mehrere unterschiedliche Zeitintervalle realisieren.
Da der Aufruf der Timer-Funktion nur einige Mikrosekunden dauert, merkt man den im normalen Programmablauf gar nicht.

wkrug
19.05.2024, 09:03
Ich würde auch das Konzept von Rabenauge anwenden.
Ich lass da immer im Timer Interrupt einen oder mehrere Variablen herunterzählen.
Wenn dann eine auf 0 ist wird ein Flag gesetzt das dann in der Hauptschleife abgefragt wird.
Im Hauptprogramm wird dann die Aktion ausgelöst und bei Bedarf das Flag wieder gelöscht und, für erneutes Ausführen, die Variable wieder neu gesetzt.
Dadurch wird das Hauptprogamm nur für die Timer Interupts unterbrochen und man kann in die Interrupt Routine mehrere Zeitintervalle einbauen.
Bei 100ms Timer Intervall ein Counter mit 10 ergibt eine Sekunde bei 100 wären es 10 Sekunden.