Ich habe meinen ATTiny 26 mit BASCOM AVR 1.11 programmiert.
Ich habe eine SUB, die einen Wert überprüft, 100ms wartet und sich dann
selber neu aufruft, bis ein bestimmter Wert erreicht ist (Reziproke).
Das sieht dann so aus:
Sub Untens() 'Sub Beginn
Waitms 100 'Warte 100ms
Porta.0 = 1 'Setze den ersten Port auf 1 (5V)
Unten = Getadc(7) 'Setzt die Variable Unten auf den Wert
If Unten < 900 Then 'Fragt den Wert ab
Call Untens() 'Ruft sich selbst noch mal aus
Exit Sub 'Beendet sich selbst
End If '
Waitms 400 'Warte 400ms
Porta.0 = 0 'Setze ersten Port auf 0 (0V)
Fertig = 1 'Setze Variable Fertig auf 1
End Sub '
Das Ganze funktioniert auch, doch wenn die Subrutine länger als 6000ms
läuft ca. 60 mal sich selbst aufruft, stürtzt das ganze Programm ab.
Ist es möglich, dass der ATTiny 26 mit solch einer Aufgabe überfordert
ist und deswegen neu startet?
Wie kann ich das Problem umgehen? Mit GoTo?
Danke, MfG Ringelkrat
PS: Konnte den Soucecode nicht als Dateianhang anhängen, da ich den auf
einem anderen PC habe.
hi
bei jedem neuen funktionsaufruf wird die rücksprungadresse auf den
stack kopiert, der deshalb immer größer wird, und irgendwann wirst du
dir damit das gesamte sram vollgestackt haben.
rekursive programme sind bei systemen mit sehr kleinem ram (stack)
nicht zu empfehlen!
nides
jeder neue Aufruf belastet den Stack - irgendwann ist Schluss. Immer ein
Problem bei Rekursionen. Man muss die max. Tiefe begrenzen, sonst kann
es aus dem Ruder laufen. Dabei auch Interrupts beachten, die kosten
auch nochmal Stack. Ebenso wichtig: wieviele Funktionsaufrufe hast du
schon hinter dir, wenn du bei der rekursiven Funktion angelangt bist?
Nützlich sind stack-end-marker, da kannst du in der Simulation sehen,
ob das stackende erreicht wurde.
Wenn ich eine While-Schleife mach, wartet er dann, oder macht er mit dem
Programm weiter (was unter der schleife steht)?
while (unten < 900)
unten = getadc(7)
waitms 100
Wend
Ringelkrat
das wär schon richtig so... er wartet, bis der adc einen wert größer
oder gleich 900 liefert und macht dann erst weiter. und der stack
bleibt wie er ist...
alternativ könnte man das ganze auch mit nem goto lösen
untens:
unten = getadc(7)
waitms 100
if unten < 900 then goto untens
(ob das von der syntax her in ordnung ist kann ich nicht sagen weil ich
seit langem kein bascom mehr benutzt hab...)
möchte mal wissen, warum immer so ein Theater mit dem GOTO gemacht wird.
Verteufelt bis zum geht nicht mehr, steht in vielen Büchern, und ganz
viele plappern es plump nach. Klar, lässt sich eigentlich immer
vermeiden, aber WARUM sollte man es vermeiden? Konnte mir noch keiner
schlüssig erklären. Sprünge (unbedingte und bedingte) Sprünge kommen in
jedem Programm vor, in Hochsprachen mehr oder weniger versteckt, in
Assembler unvermeidbar (und sogar nützlich:-)
Ich sehe ja ein, dass man nicht kreuz und quer durchs Programm springt,
aber was spricht innerhalb einer Funktion dagegen?
Und wo ist der Unterschied zwischen
switch (test){
case 1:
case 2:
}
oder
if (test==1) goto ...
else if (test==2)goto ...
?
Das 1. hat den offiziellen Segen, mehr Unterschiede erkenne ich nicht.
Wäre "Rekursion" nicht ein treffenderer Titel für das Thema gewesen?
"Reziprok" in diesem Zusammenhang lässt mich an Honoria Lucasta, der
Dowager Duchess of Denver aus Dorothy L. Sayers vorzüglichen
Kriminalromanen denken ...
Das erste ist viel übersichtlicher und zudem weniger Schreibarbeit als
das zweite.
Es hat schon seinen Sinn, dass goto "verteufelt" wird.
Es mag sein, dass es bei embedded-Systemen, wenn man mit C programmiert
ganz selten für ein Problem einfach keine elegantere/kleinere/schnellere
Lösung als goto gibt. Dann sollte man aber genau wissen, was man tut und
wieso es nicht anders geht.
Ich halte es allerdings für falsch, einem Anfänger zu goto zu raten, da
das gerade bei Beginnern zu einem schrecklichen Programmierstil führen
kann, der dann beibehalten wird.
Wirf' den Basic-Compiler weg, denn er ist Schrott.
Wenn er kein Schrott wäre, dann hätte der Compiler die Tail-Rekursion
erkannt und die Stack-Benutzung weg optimiert.
Ordentliche C-Compiler können das, z.B. der gcc... ;-)
Es gibt aber auch viele Script-Sprachen, die Tail-Rekursion-Optimierung
beherschen. Oder man verwendet gleich eine ordentliche
Programmiersprache, z.B. Ocaml oder so.
Denn so wie Du Dein Programm geschrieben hast, scheinst Du sehr
fortschrittlich (funktional und rekursiv) zu denken. Nutze die Chance
und verlasse das Imperative Lager so lange Deine Gedankengänge noch
nicht versaut sind. Du scheinst Talent zu haben!
Ich meine das übrigens ernst, nur für den Fall dass das jemand in den
falschen Hals bekommen sollte!
@crazy horse:
Versuch' die Sache mal aus einem anderem Blickwinkel zu betrachten:
1.) Für wen programmierst Du, bzw. wer liest Deinen Code?
a.) Ein Computer
b.) Ein Mensch (Du selbst zählst auch)
2.) Wer führt Dein Programm aus?
a.) Ein Computer
b.) Ein Mensch
3.) Bevorzugt der Computer ein bestimmte Variante, oder ist ihm
schurz-piep egal?
4.) Wie denkt ein Mensch, wie organisiert und plant er seine Welt?
Lebt er so:
Schritt 1: Höre auf den Wecker.
Schritt 2: Hat der Wecker geglingelt?
Schritt 3: Wenn nein, gehe zu Schritt 1.
Schritt 4: ...
Oder so:
- Solange der Wecker nicht klingelt, schlafe.
recht nettes Beispiel, aber programmtechnisch sehe ich auch da keinen
Unterschied, denn bei deinem "solange der Wecker nicht klingelt"
machst du ja auch nichts anders, als zu horchen, ob der Wecker nun
klingelt und dann zurückzuspringen (oder eben nicht).
Für Schleifen, gleich welcher Art, nutze ich goto auch nicht,
ebensowenig käme ich auf die Idee, eine switch-Anweisung zu ersetzen,
einen logischen Unterschied sehe ich aber nicht.
Allerdings kommen mir immer wieder mal verschachtelte Schleifen unter,
und dann scheint mir goto sehr nützlich.
Sagen wir mal 3 Schleifen, Abbruchkriterien für das gesamte Konstrukt
können in allen 3 Schleifen entstehen. Wie löst du das? Die äussere
Schleife kann ich mit break (was ja auch wieder nur ein verkappter goto
ist) verlassen - was mache ich, wenn ich auch aus der 2. oder 3.
Schleife heraus das Ganze verlassen will?
variable setzten, in allen schleifen abfragen und wieder break. ist aber
auch murks, da hast du vollkommen recht. da würd ich auch eher goto
nehmen.
mitlerweile wird einem ja leider sogar erzählt, dass man sogar ganz
ohne break/continue auskommen soll, wäre ja alles
keine 'strukturierte programmierung'* mehr
* hiermit schlage ich dieses wort als computer-unwort des jahres vor
> bei deinem "solange der Wecker nicht klingelt"> machst du ja auch nichts anders, als zu horchen, ob der Wecker> nun klingelt und dann zurückzuspringen (oder eben nicht).
Also ich schlafe nicht so. Das passiert interruptgesteuert und nicht
gepollt.
Schön gesagt, aber wenn Du schläfst dann 'achtet' ja dein Körper auch
auf die Umgebungsgeräusche. Sonst würdest du ja vom Klingeln nicht
aufwachen. Mit diesem 'achten auf Umgebung' ist auch nur die abfrage
eines sensors gemeint. um das ergebnis der abfrage zu interpretieren
muss du auch (ggf in eine variable packen) und dann den wert mit einer
if oder while bedingung überprüfen.
damit heißt es doch auch dass du also ständig deinen umgebungssensor
abfragst, oder eine variable, die die schlaf-subroutine unterbricht.
wie auch immer, ihr werdet schon sehen, wie roboter besser schlafen
werden als menschen.... ^^
Guten Morgen, Ringelkrat