Forum: Mikrocontroller und Digitale Elektronik Verzögerung in der for-schleife


von Berni S. (bernycontroller1)


Lesenswert?

Abend Kollegen ;)

Ich möchte in meinem Programm etwa 1 Sekunde warten. Dazu nehme ich 
meine for-Schleife "for(i=0; i<Endwert; i++)".

Den richtigen Endwert will ich dann durch ausprobieren herausfinden.
Mein Chef meinte aber das ist keine gute Idee.

Was meint ihr könnte den hier schief gehen?

Gruß
Bernie

von DraconiX (Gast)


Lesenswert?

Berni S. schrieb:
> Abend Kollegen ;)
>
> Ich möchte in meinem Programm etwa 1 Sekunde warten. Dazu nehme ich
> meine for-Schleife "for(i=0; i<Endwert; i++)".
>
> Den richtigen Endwert will ich dann durch ausprobieren herausfinden.
> Mein Chef meinte aber das ist keine gute Idee.
>
> Was meint ihr könnte den hier schief gehen?
>
> Gruß
> Bernie

Dein Chef hat recht... Der µC, PC oder was auch immer bleibt 
ausschließlich in dieser Schleife, die ganze Sekunde lang, und der 
restliche Prozessor ist nun ausschließlich damit beschäftigt und kann 
keine weiteren Aufgaben mehr erledigen. Desweiteren, unterbricht ein 
Interrupt diese Schleife - dann stimmt dein "händisch" Zählen nicht mehr 
und dein Timing ist dahin.

Richtig macht man sowas mit einem Timer oder einem Pseudo-Threading.

von Nick S. (c0re)


Lesenswert?

Berni S. schrieb:
> Was meint ihr könnte den hier schief gehen?

Ohne Kontext vieles und gar nichts.
Bei einem Raketenabwehrsystem bedeutet eine Sekunde nichts tun, also 
wirklich GAR NICHTS, weil es gerade eine variable hochzählen muss, 
vielleicht den Tod.

Bei einer elektronischen Grußkarte wäre es wohl verschmerzbar.

von Berni S. (bernycontroller1)


Lesenswert?

Super danke für deine Hilfe :)

von Klaus Dieter (Gast)


Lesenswert?

Der Compiler wird dir das sowieso rausschmeißen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Berni S. schrieb:
> Ich möchte in meinem Programm etwa 1 Sekunde warten.

Das Programm ist für welche Zielplattform?

von S. Landolt (Gast)


Lesenswert?

> Der Compiler wird dir das sowieso rausschmeißen.
Eben erst:
Beitrag "AVR Studio 6 und 7 stoppen nicht im Simulator beim debuggen"

von Yalu X. (yalu) (Moderator)


Lesenswert?

Berni S. schrieb:
> Ich möchte in meinem Programm etwa 1 Sekunde warten.

Wenn das komplette Programm eine ganze Sekunde wirklich nur warten soll,
kannst du die CPU schlafenlegen und damit Strom sparen. Die meisten
Mikrocontroller bieten diese Möglichkeit und ebenso einen Timer, der als
"Wecker" konfiguriert die CPU nach einer vorgegenen Zeitdauer wieder
aufweckt.

Führt die CPU während der Wartezeit interruptgesteuert noch andere Dinge
im Hindergrund aus, ist die Warteschleife ebenfalls eine schlechte
Lösung, weil die Zeit für ihre Abarbeitung vom Rechenaufwand für die
Hindergrundaktivitäten abhängt.

von Jacko (Gast)


Lesenswert?

Wenn du ein Programm erstellen willst, dass nur ein wenig
zeitbezogen arbeiten soll, dann lässt du in einer
Timer-Interrupt-Service-Routine z.B. alle 10 ms eine
Variable COUNT_10MS hochzählen.

In dieser Routine
- prüfst du, ob Tasten für eine Mindestzeit gedrückt, oder
  losgelassen wurden (Entprellung...) und vermerkst das in
  zugeordneten Variablen.
- prüfst du, ob irgendwelche regelmäßigen Aktionen (Update
  der Anzeige, ...) an der Reihe sind, was in zugeordneten
  Variablen dem Hauptprogramm gemeldet wird.
- zählst du Warte-Variablen, solange sie größer NULL sind,
  um 1 herunter.

Das Hauptprogramm besteht aus einer Schleife, in der diese
zugeordneten Variablen abgefragt werden und reagiert dann
entsprechend.

Wenn eine Aktion in 1 s erforderlich ist, setzt du eine
Warte-Variable auf 100 (100 x 10 ms = 1s).

In der Schleife des Hauptprogramms schaust du regelmäßig
nach, ob diese Warte-Variable schon NULL geworden ist.
In der Zwischenzeit kann der µC weiterhin alles andere
schön abarbeiten...

von Carl D. (jcw2)


Lesenswert?

Jacko schrieb:
> Wenn du ein Programm erstellen willst, dass nur ein wenig
> zeitbezogen arbeiten soll, dann lässt du in einer
> Timer-Interrupt-Service-Routine z.B. alle 10 ms eine
> Variable COUNT_10MS hochzählen.
>
> In dieser Routine
> - prüfst du, ob Tasten für eine Mindestzeit gedrückt, oder
>   losgelassen wurden (Entprellung...) und vermerkst das in
>   zugeordneten Variablen.
> - prüfst du, ob irgendwelche regelmäßigen Aktionen (Update
>   der Anzeige, ...) an der Reihe sind, was in zugeordneten
>   Variablen dem Hauptprogramm gemeldet wird.
> - zählst du Warte-Variablen, solange sie größer NULL sind,
>   um 1 herunter.
>.
> Das Hauptprogramm besteht aus einer Schleife, in der diese
> zugeordneten Variablen abgefragt werden und reagiert dann
> entsprechend.
.
> Wenn eine Aktion in 1 s erforderlich ist, setzt du eine
> Warte-Variable auf 100 (100 x 10 ms = 1s).
.
> In der Schleife des Hauptprogramms schaust du regelmäßig
> nach, ob diese Warte-Variable schon NULL geworden ist.
> In der Zwischenzeit kann der µC weiterhin alles andere
> schön abarbeiten...

Und um Folgefragen gleich zu beantworten, die Variable WDR so definiert:
1
  unsigned volatile count_MS;
damit der Compiler auf die Idee kommt, den Wert jedesmal neu lesen zu 
müssen.

von A. S. (Gast)


Lesenswert?

Wenn Du kompletter Anfänger wärest und Du die Schleife für irgendwas zum 
ausprobieren brauchst, so wäre das mal kurzfristig vielleicht ok.

Da Du aber mit Deinem "Chef" darüber sprichst ... bist Du als 
Programmierer oder gar Entwickler angestellt? Dann wäre es höchste Zeit, 
mehr über Deine Kenntnisse zu schreiben um an den Basics zu arbeiten. 
Auf dem Niveau wirst Du einem Ansatz mit timer und Interrupt vermutlich 
nicht direkt folgen können.

Ich hoffe daher, Du machste ein Praktikum. Aber auch dann ist es 
hilfreich zu wissen, was Du schon kannst und was Du erforschst, um Dich 
nicht zu über- oder zu unterfordern.

von Christian K. (Gast)


Lesenswert?

Das war schon 1980 Murks, als viele Spiele u.a. Tetris und auch der MS 
Flugsimulator auf der nächst schnelleren Kiste (z.B. AT) nicht mehr 
nutzbar waren. Schnell die nächste Rasperry Pi Generation oder ein 
Prozessor mit höherem Takt eingesetzt und deine "tu nichts" Schleife 
stimmt nicht mehr. So gut wie jedes System bietet hierzu einen Timer, 
den du setzen und abfragen kannst, wann deine Sekunde abgelaufen ist. 
Stöber mal zu deinem Prozessor im Internet, sollte sich etwas finden.

Mit freundlichen Grüßen
Christian

von Axel S. (a-za-z0-9)


Lesenswert?

Berni S. schrieb:

> Ich möchte in meinem Programm etwa 1 Sekunde warten. Dazu nehme ich
> meine for-Schleife "for(i=0; i<Endwert; i++)".

eine typische Anfänger-"Lösung"

> Den richtigen Endwert will ich dann durch ausprobieren herausfinden.
> Mein Chef meinte aber das ist keine gute Idee.

Da hat er recht. Und er hätte auch dann Recht, wenn er nicht dein Chef 
wäre.

> Was meint ihr könnte den hier schief gehen?

Das ist nicht unbedingt der Punkt. Auch ohne daß etwas wirklich "schief 
geht" ist es trotzdem aus vielerlei Gründen eine blöde Idee:

1. die Schleife muß kalibriert werden. Das ist ein zusätzlicher 
Arbeitsschritt.

2. die Kalibrierung stimmt dann aber nur für a) diesen µC bei b) 
dieser Taktfrequenz und c) diesen Compiler mit d) diesen 
Optimierungseinstellungen.

a) Portierbarkeit auf einen anderen µC ist nicht gegeben. Stell dir vor, 
der µC wird in ein paar Jahren abgekündigt und das Produkt muß auf einen 
anderen µC umgestellt werden. Fängst du wieder bei 1. an.

b) je nachdem wie genau deine Verzögerung sein muß, ist vielleicht sogar 
schon die Abweichung der Taktfrequenz zu groß.

c) einen neuen Compiler kannst du schnell mal kriegen, z.B. weil die 
neue Version einen lästigen Bug fixt. Wenn du ein Betriebssystem mit 
automatischen Updates nutzt, merkst du das womöglich gar nicht gleich. 
Oder stell dir mal vor, in 10 Jahren soll an dem Produkt noch was 
geändert werden, aber der alte Compiler ist nicht mehr aufzutreiben.

d) genau genommen würde ich von jedem halbwegs aktuellen Compiler 
erwarten, daß er die Schleife komplett wegoptimiert - es sei denn du 
hast die Optimierung ausgeschaltet. Allerdings beobachte ich in der 
Praxis eher das Gegenteil: das Programm wird im Laufe der Entwicklung 
immer langsamer und die Entwickler drehen als erstes mal die Optimierung 
im Compiler hoch. Und bumms - schon stimmt deine tolle Schleife nicht 
mehr.

3. während dieses busy waiting (das ist der Fachbegriff) verbraucht 
dein µC das Maximum an Strom um - nichts zu tun. Wobei: stimmt nicht. Er 
tut nicht "nichts", sondern er rennt vollkommen sinnlos im Kreis. 
Insbesondere kann er während dieser Zeit auch nichts sinnvolles tun. Das 
ist wie wenn du während des Wartens auf den Geschirrspüler die ganze 
Zeit etwas sinnloses tun würdest. Sagen wir: den Geschirrschrank immer 
wieder aus- und wieder einräumen. Eine ganze Stunde lang, bis der 
Geschirrspüler fertig ist. Käme dir das sinnvoll vor? Würdest du die 
ZEit nicht lieber für eine sinnvolle Arbeit verwenden? Oder, wenn du 
partout nichts zu tun hast, ein Nickerchen auf der Couch machen?

Der korrekte Weg, eine Verzögerung zu implementieren, nutzt einen der 
(meist vielen) Timer im µC. Das ist ein Hardware-Zähler, der einen 
(konfigurierbaren) Takt zählt und bei einem (konfigurierbaren) 
Zählerstand oder beim Überlauf einen Interrupt auslöst. Das 
funktioniert vollkommen in Hardware, unabhängig davon was der µC gerade 
tut (insbesondere auch, wenn er in einen Stromsparmodus versetzt wurde) 
und mit hoher Genauigkeit. Wenn man dem Timer nicht gleich seinen 
eigenen, unabhängigen Takt gibt, z.B. von einem Uhrenquarz, dann kennt 
man den Takt zumindest und muß im Fall des Falles nur eine Konstante im 
Programm ändern.

PS: zu 2. gibt es eine nette Anekdote. Die Programmierer bei Borland 
wollten besonders schlau sein und haben in Turbo Pascal eine 
automatische Kalibrierung ihrer Verzögerungsschleife eingebaut. Dazu 
haben sie den System-Timer ausgelesen (der zählt alle 55ms um 1 hoch), 
dann die Schleife mit 1 Million [1] Durchläufen rennen lassen und den 
System-Timer wieder ausgelesen. Aus der vergangenen Zeit (Differenz 
Timerwerte mal 55ms) konnten sie dann ausrechnen, wieviel 
Schleifendurchläufe sie für eine bestimmte Zeit brauchten.

Blöderweise wurde die PC irgendwann so schnell, daß sie die Schleife in 
weniger als 55ms schafften. Die beiden Timerwerte waren gleich und bei 
der Berechnung kriegte man einen "Division durch Null" Fehler (gerade 
nochmal nachgegoogled: "runtime error 200" war das).

Das nur zur Warnung, falls du diese Idee auch hattest.

[1] 1 Million ist ein Beispiel. Ich weiß nicht wieviele Zyklen sie 
wirklich gemacht haben. Ist auch nicht von Belang. Es war halt eine 
Zahl, bei der sie eine Verzögerung im Bereich von maximal einer Sekunde 
erwartet haben.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Borland..."runtime error 200" war das.

Wie habe ich den gehasst! Ich musste einige uralte Programme von längst 
verschollenen Autoren auf aktuelle Frameworks umschreiben und neu 
compilieren - denn ein Bugfix von Borland war Monate zu spät verfügbar.

Das war für mich auch die letzte Arbeit mit Borland Produkten.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.