PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : delay-Ersatz ohne loop-Bremse



frabe
07.04.2020, 13:32
Hallo zusammen.

( Dieses Bsp ist nur ein Teilschritte zur späteren Tastenentprellung (mehrerer Tasten über Interrupt) ohne delay(),
daher bitte NUR schrittweise (Konzept)Hinweise, und KEINE fertigen Lösunge, sonst lerne ich das nie... )

1. Primär geht es mir darum, Zeitberechnungen in eine Funktion auszulagern, ohne den loop() mit delay() etc. zu Bremsen!
2. Mehrer parallele Zeitberechnungen möglich.
3. Möglichst nur eine Zeitberechnungs-Funktion für alle Anwendungen, auf die Jeder, zu jeder Zeit zugreifen kann.

Bsp:
loop(){
//kein delay()
//kein while()
...macht irgend etwas
Start: LED1, ein, für 20ms, danach aus für 1000ms
...macht irgend etwas
Start: LED2, ein, für 100ms, danach aus für 100ms
...macht irgend etwas
}
Alle loop()-Schritte sind unabhängig voneinander.
Eine Funktion(), Bsp.Ausg() als "EierLegendeWollMilchSau". Problem ist hierbei, dass ich nur eine "volatile long msStart=0;" zur Verfügung habe.


Mein (falscher) Ansatz, ohne delay();


void loop() {
...
Ausg(LED1Pin, 1, 20);
...
Ausg(LED1Pin, 0, 1000);
...
Ausg(LED2Pin, 1, 100);
...
Ausg(LED2Pin, 0, 100);
...
}

void Ausg(int Pin, int OnOff, long Dauer) { // Dauer=0...dauerhaft, ohne Zeitlimit
if(Dauer != 0) {
if(msStart == 0) {
msStart = millis();
}
if(millis() <= msStart + Dauer) {
digitalWrite(Pin, OnOff);
}
else {
digitalWrite(Pin, !OnOff);
msStart = 0;
}
}
else {
digitalWrite(Pin, OnOff);
}
}


PS: Besser kann ich meine Gedanken gerade nicht formulieren. Daher bitte Fragen, wenn euch irgend eine Info fehlt.

- - - Aktualisiert - - -

PS:
Für jeden Ein/Ausgang eine eigene Funktion?
... mit eigenem Zähler?
... und eigener volatile long Variablen?
Sollte das eine gute Lösung sein?
Statt dessen würde ich gerne alles in kleine und kleinste Funktionsblöcke unterteilen.

Holomino
07.04.2020, 13:59
Beispiel:
PortBit // Parameter (Bitnummer)
OffTime // Parameter (in millis)
OnTime // Parameter (in millis)
InitialState // Parameter (On/Off)
*Callback // Parameter (ggf. aufzurufender Funktionszeiger, wenn mehr als der Port getoggelt werden soll)
State //Puffer (aktuell On/Off)
Start //Puffer (letzter Flankenwechsel-millis)

So etwas kannst Du Dir als Strukturtyp definieren und für jeden Ausgang instanziieren. Die Parameter definiert (setzt) man beim Applikationsstart einmalig. Die Puffervariablen werden im loop-Durchlauf gebraucht.

Anstelle der einzelnen Infos steckt man dann einfach einen Zeiger auf die Strukturinstanz in die Ausg(…)-Funktion.

In C++ geht's komfortabler mit Klassen (wobei ich nicht glaube, dass Du die Objektorientierung schneller verstehst als Strukturen).

frabe
07.04.2020, 14:30
Danke erst einmal für Rü


Beispiel:
In C++ geht's komfortabler mit Klassen (wobei ich nicht glaube, dass Du die Objektorientierung schneller verstehst als Strukturen).

Tatsächlich gehts mir um universelle Funktionen, vorallem aber Einfachheit. Damit ich die nach 1/2 Nichtstun schnell rein komme.


Beispiel:
So etwas kannst Du Dir als Strukturtyp definieren und für jeden Ausgang instanziieren. Die Parameter definiert (setzt) man beim Applikationsstart einmalig. Die Puffervariablen werden im loop-Durchlauf gebraucht.

Klingt erst einmal kompliziert!
Wenn das dennoch ein einfacher Weg ist, werde mich mit dem Thema "Struktur" und "Funktionszeiger" beschäftigen.

Holomino
07.04.2020, 15:02
Sieh es einmal so:
Was Du gelernt hast, hast Du im Sack.
Programmiersprachenentwickler bauen keine Features, um etwas zu verkomplizieren, sondern um es zu vereinfachen, d.h. nach jeder neu gelernten syntaktischen Feinheit geht es Dir besser, weil Du Sachen im Code einfacher, schneller, kürzer und damit auch verständlicher ausdrücken kannst.


(P.S.: Natürlich hauen Programmiersprachenentwickler auch manchmal daneben, aber nach knapp 50 Jahren kannst Du davon ausgehen, dass das Konstrukt "Struktur" ausgereift ist.
C++ ist syntaktisch nicht komplizierter, aber umfangreicher)

HaWe
07.04.2020, 15:51
am einfachsten könnte man dazu mit elapsedMillis() beginnen:
https://playground.arduino.cc/Code/ElapsedMillis/
https://github.com/pfeerick/elapsedMillis/archive/master.zip




/*
Simple demo of using all the timing helpers elapsedMillis makes available.

Either attach LEDs with series resistors to the indicated pins, or a
six led / six bit 'Chartreuse' module plugged into pins 8 through GND.

Wired up in order, the leds have a nice walking/counting effect.

This example code is in the public domain.
*/

#include <elapsedMillis.h>

//declare these global if you don't want them reset every time loop runs
elapsedMicros LED1micro;
elapsedMicros LED2micro;
elapsedMillis LED3millis;
elapsedMillis LED4millis;
elapsedSeconds LED5seconds;
elapsedSeconds LED6seconds;

const int LED1 = 8;
const int LED2 = 9;
const int LED3 = 10;
const int LED4 = 11;
const int LED5 = 12;
const int LED6 = 13;

// delay between blinks of the LED
unsigned long LED1_Interval = 62500;
unsigned long LED2_Interval = 125000;
unsigned int LED3_Interval = 250;
unsigned int LED4_Interval = 500;
unsigned int LED5_Interval = 1;
unsigned int LED6_Interval = 2;

void setup()
{
// initialize the LED pins as outputs
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
pinMode(LED4, OUTPUT);
pinMode(LED5, OUTPUT);
pinMode(LED6, OUTPUT);
}

void loop()
{
if (LED1micro >= LED1_Interval)
{
digitalWrite(LED1, !(digitalRead(LED1))); // toggle the LED state
LED1micro = 0; // reset the counter to 0 so the counting starts over...
}

if (LED2micro >= LED2_Interval)
{
digitalWrite(LED2, !(digitalRead(LED2))); // toggle the LED state
LED2micro = 0; // reset the counter to 0 so the counting starts over...
}

if (LED3millis >= LED3_Interval)
{
digitalWrite(LED3, !(digitalRead(LED3))); // toggle the LED state
LED3millis = 0; // reset the counter to 0 so the counting starts over...
}

if (LED4millis >= LED4_Interval)
{
digitalWrite(LED4, !(digitalRead(LED4))); // toggle the LED state
LED4millis = 0; // reset the counter to 0 so the counting starts over...
}

if (LED5seconds >= LED5_Interval)
{
digitalWrite(LED5, !(digitalRead(LED5))); // toggle the LED state
LED5seconds = 0; // reset the counter to 0 so the counting starts over...
}

if (LED6seconds >= LED6_Interval)
{
digitalWrite(LED6, !(digitalRead(LED6))); // toggle the LED state
LED6seconds = 0; // reset the counter to 0 so the counting starts over...
}
}

frabe
07.04.2020, 16:12
struktur in c (https://www.mikrocontroller.net/articles/Strukturen_in_C)
Weitestgehend Verstanden!
Macht bei großen, zusammenhängenden Datenvariablen (Bsp: Adressbuch) Sinn.

Aber was hat das mit meinem Bsp. "parallele Zeitberechnungen" zu tun?
Sind da nicht eher static-Variablen und Referenzen (&Variable) gefragt?

Rabenauge
07.04.2020, 16:15
Viel einfacher: das mitgelieferte Beispielprogramm "blinkWithoutDelay" ansehen und verstehen-das enthält alles, was man braucht.
Keine Extra-Lib's, keine Zaubertricks.
Auf diesem einfachen Beispiel basieren meine Timer, die ich in letzter Zeit ja schon einige Male erwähnt hatte..und die funktionieren prächtig, wenn man sie richtig einsetzt.

Moppi
07.04.2020, 16:46
https://www.arduino.cc/en/tutorial/BlinkWithoutDelay

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

MfG

Siro
07.04.2020, 17:08
Ich habe da folgende Vorgehensweise:




typedef unsigned int TTimeValue; // ich nehme 16 Bit Werte für die Millisekunden Zeiten */

typedef volatile struct // in dieser Struktur werden alle Zeitwerte verwaltet
{
TTimeValue Led1BlinkTime;
TTimeValue Led2BlinkTime;
TTimeValue KeyTime;

/* hier kann man beliebig erweitern */
} TimerStruct;

volatile TimerStruct Timers; // das ist die gesamte Timerstruktur (die Instanz)

//----------------------------------------------------------------
// der 1 Millisekunden Timer Interrupt:
// hier wird jeder Zeitwert, solange er noch nicht 0 ist, um eins erniedrigt

void TIMER0_IRQHandler (void)
{ int i;
TTimeValue *p = (TTimeValue*) &Timers; /* initialize p to startaddress of the structure */

for (i=0; i< (sizeof(Timers) / sizeof(TTimeValue)); i++)
{
if (*p) /* if current addressed counter value is greater zero then */
--*p; /* decrement the counter value by one */
p++; /* p points now to the next counter value */
}
}
//----------------------------------------------------------------
// Jetzt kommen diverse Funktionen die ausgeführt werden sollen:

void DoLed1(void)
{ static char BlinkPhase;

if (Timers.Led1BlinkTime) return; // es gibt noch nichts zu tun, Zähler noch nicht 0
if (BlinkPhase)
{
// Led1On();
Timers.Led1BlinkTime = 250; // 250 ms ein
} else
{
// Led1Off();
Timers.Led1BlinkTime = 100; // 100 ms aus
}
BlinkPhase = ! BlinkPhase; // BlinkPhase umdrehen
}


void DoLed2(void)
{ static char BlinkPhase;

if (Timers.Led2BlinkTime) return; // es gibt noch nichts zu tun, Zähler noch nicht 0
if (BlinkPhase)
{
// Led2On();
Timers.Led1BlinkTime = 500; // 500 ms ein
} else
{
// Led2Off();
Timers.Led1BlinkTime = 250; // 250 ms aus
}
BlinkPhase = ! BlinkPhase; // BlinkPhase umdrehen
}

void DoKeys(void)
{
if (Timers.KeyTime) return; // noch nix zu tun
Timers.KeyTime = 20; // nächste Abfrage in 20ms

// Tastenstatus auswerten usw.....
}

void main(void) // Hauptprogramm:
{
while (1)
{
DoLed1();
DoLed2();
DoKeys();
// ....usw....
}
}



aber viele Wege führen zum Ziel, da hat sicher jeder eine andere Lösung...

Siro

Holomino
07.04.2020, 18:28
struktur in c (https://www.mikrocontroller.net/articles/Strukturen_in_C)
Weitestgehend Verstanden!
Macht bei großen, zusammenhängenden Datenvariablen (Bsp: Adressbuch) Sinn.

Aber was hat das mit meinem Bsp. "parallele Zeitberechnungen" zu tun?
Sind da nicht eher static-Variablen und Referenzen (&Variable) gefragt?

Die Verwendung einer Struktur hat nichts mit Größe oder Anzahl zu tun, sondern damit, die Variablen für einen Funktionsbaustein sinnvoll zusammenzuhalten und erweiterbar zu gestalten, so dass das weitermachen auch bei einem Fehlschlag oder einer neuen Anforderung noch Spaß macht! Guter Code entsteht niemals durch Zwang von Außen, sondern aus der einfachen Erkenntnis:
Wenn es machbar ist und ein Erfolg winkt, tue ich es. Damit es machbar ist und bleibt, vermeide ich Codegräber und schreibe wartbaren Code.


Dein Eingangsbeispiel zeigt doch schon drei Parameter auf. Mit denen funktioniert es nicht, da fehlt noch was. Das hast Du ja selber gemerkt.
Das kannst Du jetzt mit zusätzlichen Übergabeparametern ergänzen. Nur musst Du dann auch die Variablen einzeln für die Übergabe definieren.
Das gibt spätestens nach der 3. Änderungswelle ein heilloses, unwartbares Chaos. Bei jeder Erweiterung musst Du den aufgerufenen Funktionsrumpf ändern, alle Aufrufe anpassen und noch neue Variablen definieren.
Mit Strukturen erweiterst Du nur den Strukturtyp, setzt die Initialwerte der Definitionen (Variablen) und kümmerst Dich um das Wesentliche - den Code in Deiner Funktion.

Moppi
07.04.2020, 18:29
Hallo Siro,

das: "sizeof(Timers) / sizeof(TTimeValue)" würde ich aus der For-Schleife raus nehmen und das Ergebnis in eine Variable packen.


int irgendwas = sizeof(Timers) / sizeof(TTimeValue);
und die Schleife dann so ändern:

for (i=0; i< irgendwas; i++)
Evtl. funktioniert das ja auch noch mit "const" als Konstante, das weiß ich nicht an der Stelle.

Es sei denn, der Compiler würde das selbst optimieren. Sonst hättest Du bei jedem Schleifendurchlauf die Berechnung drin, deren Ergebnis sich nie ändert. Mit den verlorenen Taktzyklen kann man was anderes anfangen.

MfG

HaWe
07.04.2020, 18:55
Es sei denn, der Compiler würde das selbst optimieren. Sonst hättest Du bei jedem Schleifendurchlauf die Berechnung drin, deren Ergebnis sich nie ändert. Mit den verlorenen Taktzyklen kann man was anderes anfangen.

so doof ist kein moderner C/C++-Compiler

Siro
08.04.2020, 01:42
@Moppi:

Hatte ich Anfangs auch bedenken, aber wie HaWe schon schreibt, der Compiler setzt da eine Konstante ein.
Er macht selbst die Vorrausberechnung, das habe mir im Assemblercode vei verschiedenen Compilern jeweils angesehen.
Grade bei meinen kleinen PICs mit wenig Speicher, bin ich da immer am gucken was der Compiler für Code erzeugt,

Siro

Moppi
08.04.2020, 08:14
Danke Siro, für die Rückmeldung!


aber wie HaWe schon schreibt

Der schreibt ... :

so doof ist kein moderner C/C++-Compiler


MfG

gekürzt, Manf
Die Bermerkung ist viel zu weitreichend, wen könnte man denn hier als Vorbild nennen?

frabe
08.04.2020, 16:19
Ich danke euch Allen erst einmal vielmals für die konstruktiven Antworten und Hinweisen.
Mit diesem Bsp spiele ich derzeit - muss dann in den nächsten Tagen auf meine zukünftig mutiplen Bedürfnisse angepasst werden. (https://www.arduino.cc/en/tutorial/BlinkWithoutDelay)

Rabenauge
08.04.2020, 23:09
Sag ich doch dauernd...bemüh mal die Suche hier im Forum, ich hatte neulich erst Auszüge eines Codes gepostet, den ich benutze- der basiert auf dem Beispiel, kann aber _etwas_ mehr als nur ein Lichtlein blinken lassen.

Man muss sich aber darüber im klaren sein, dass diese Herangehensweise auch ihre Grenzen hat.

frabe
09.04.2020, 12:13
Nebenfrage:

Gibt es zwischen den beiden folgenden Codes irgend einen sinnvollen Unterschied?

Ich sehe häufig;


if (millis() - StartZeit >= Dauer)


kann aber folgendes leichter/schneller lesen;


if (millis() >= StartZeit + Dauer)

HaWe
09.04.2020, 12:55
Nebenfrage:

Gibt es zwischen den beiden folgenden Codes irgend einen sinnvollen Unterschied?

Ich sehe häufig;

kann aber folgendes leichter/schneller lesen;

beides ist gleich gut und gleich schnell, reine Geschmacksache.