Hallo,
mit der Arduino-IDE habe ich ein kleines Programm für einen ATtiny84
erstellt. Das Programm verwendet die Funktion delay(), die dafür den
Timer0 des ATtiny84 benutzt.
Außerdem verwendet das Programm den Timer1 im CTC-Mode.
Das läuft auch ganz gut, nur manchmal bleibt das Programm hängen. Wo
genau das passiert ist nicht klar.
Schließlich habe ich die delay()-Funktion auskommentiert und zum Test
durch eine einfache Warteschleife ersetzt.
Ergebnis: Das Programm bleibt nicht hängen.
Gegenprobe: Die delay()-Funktion wird benutzt, Timer1 bleibt
abgeschaltet.
Ergebnis: Das Programm bleibt nicht hängen.
Schlussfolgerung: Timer0 und Timer1 hängen irgendwie zusammen bzw.
beeinflussen sich.
Natürlich will ich lieber die delay()-Funktion benutzen als die
Warteschleife. Vor allem aber will ich verstehen, wo sich Timer0 und
Timer1 in die Quere kommen.
Im Datenblatt
http://www.atmel.com/images/doc8006.pdf
steht auf Seite 115 "Timer0 and Timer1 share the same prescaler module".
Hängt das damit zusammen?
Es ist auch nicht so wie bei andern ATtiny-Controllern, dass sich
mehrere Timer die gleichen Control-Register teilen.
Vor allem aber: Wie kann ich also die delay()-Funktion zusammen mit
Timer1 in friedlicher Koexistenz verwenden?
Danke.
Gruß
Sören
Was zu sehen ist: Die Interrupts werden abgeschaltet:
cli()
Wo aber werden sie wieder eingeschaltet?
Evtl. hängt es ja damit zusammen, dass mein Programm hängen bleibt, dass
die delay()-Funktion Interrupts abschaltet aber unter gewissen Umständen
nicht wieder aktiviert.
@ Sören (Gast)
>Außerdem verwendet das Programm den Timer1 im CTC-Mode.>Schlussfolgerung: Timer0 und Timer1 hängen irgendwie zusammen bzw.>beeinflussen sich.
Nö.
>Natürlich will ich lieber die delay()-Funktion benutzen als die>Warteschleife.
Beides ist nicht sonderlich leistungsfähig. Wenn du schon Timer 1 laufen
hast, mach es doch gleich richtig. Siehe Multitasking.
Anstatt der vorgegebenen Funktion delay() verwende ich nun meine eigene:
1
voidmyDelay(unsignedlongms)
2
{
3
uint32_tstart=micros();
4
5
while(ms>0){
6
// yield();
7
while(ms>0&&(micros()-start)>=1000){
8
ms--;
9
start+=1000;
10
}
11
}
12
}
Der einzige Unterschied ist, dass yield() auskommentiert ist.
Jetzt bleibt nichts mehr hängen.
Auf der Suche nach diesem ominösen yield() und was dort passieren
könnte, bin ich nicht weiter gekommen. Die Ursache ist also noch immer
unklar.
Aber so funktioniert das erst mal.
Ja, das habe ich auch gelesen.
Der Aufruf von yield() erlaubt es, während des Wartens andere Aufgaben
auszuführen. Offenbar hängt der Header scheduler.h damit zusammen.
Aber weiter habe ich nichts rausgefunden, keine Implementierung
gefunden.
Dort wäre wahrscheinlich ersichtlich, warum dieser Aufruf zu Problemen
führt.
Sören schrieb:> Ja, das habe ich auch gelesen.> Der Aufruf von yield() erlaubt es, während des Wartens andere Aufgaben> auszuführen. Offenbar hängt der Header scheduler.h damit zusammen.> Aber weiter habe ich nichts rausgefunden, keine Implementierung> gefunden.> Dort wäre wahrscheinlich ersichtlich, warum dieser Aufruf zu Problemen> führt.
Die Funktion yield() gibt es erstmal gar nicht.
Die muß vom Anwender erstellt werden und wird dann von dem Arduinozeugs
in das Arduino-Delay eingebaut, um während des Zeitverdödelns beim Delay
doch noch etwas sinnvolles zu tun.
Wenn diese Funktion leer ist, ist es umso erstaunlicher, dass mit dieser
Funktion mein Programm reproduzierbar nach ein paar Sekunden hängen
bleibt. Kommentiere ich sie aus, läuft das Programm stundenlang (über
Nacht durchgelaufen, dann wieder von heute Morgen bis jetzt).
Funktion wieder rein kommentieren: Programm bleibt hängen.
Seltsam...
Sören schrieb:> Kommentiere ich sie aus
Du hast das ganze Prinzip nicht verstanden. Du kommentierst keine
Funktion aus. Du kommentierst den Aufruf einer Funktion aus. Diese
Funktion ist nicht dazu da, von dir aufgerufen zu werden. Du musst diese
Funktion erstellen.
1
voidyield(void)
2
{
3
BerechnePrimzahlen();
4
}
Diese wird dann ins Delay eingebaut, damit die Zeit mit sinnvollen
Dingen, statt mit Auf-der-Stelle-treten verbracht wird.
Du zäumst das Pferd von hinten auf und wunderst dich, daß der Gaul
rückwärts läuft und du vom Sattel fällst.
Jetzt verstehe ich tatsächlich, wie diese yield()-Funktion gedacht ist.
Aber:
In der Vergangenheit hatte ich schon häufig die eingebaute
delay()-Funktion eingesetzt. Diese yield()-Funktion hatte ich nie
implementiert.
Und jetzt in Zusammenspiel von delay() mit Timer1 bleibt plötzlich das
Programm hängen. Das ließ sich wie gesagt dadurch beheben, dass yield()
auskommentiert wurde.
Ja, es ist nur der Aufruf einer Funktion, die nicht implementiert wurde.
Warum hat das dann solche Auswirkungen?
Sören schrieb:> Aber:> In der Vergangenheit hatte ich schon häufig die eingebaute> delay()-Funktion eingesetzt. Diese yield()-Funktion hatte ich nie> implementiert.
Zitat: "Nichts ist perfekt, und auch ein Compiler ist es nicht."
Sören schrieb:> Jetzt verstehe ich tatsächlich, wie diese yield()-Funktion gedacht ist.>> Aber:
Scheinbar nicht. Thomas hat es dir bereits beschrieben
yield() ist dazu gedacht in einem delay()-Aufruf noch
weiterrechnen zu können damit du nicht auf der Stelle
treten lässt.
Thomas E. schrieb:> Diese> Funktion ist nicht dazu da, von dir aufgerufen zu werden. Du musst diese> Funktion erstellen.
Ein einfaches Beispiel zum Verständnis lässt sich auch
einfach in g* oder in derArduino-Welt finden:
1
unsignedlongyieldCount=0;
2
3
voidsetup()
4
{
5
Serial.begin(9600);
6
}
7
8
voidyield(void)
9
{
10
Serial.println(++yieldCount);
11
}
12
13
voidloop()
14
{
15
delay(500);
16
}
Du siehst damit dass <yieldCount> verändert wird (obwohl in der
delay() gewartet wird), und das wesentlich häufiger als das
delay es vermuten lässt.
Ich habe inzwischen schon verstanden, wozu dieser Aufruf von yield() in
der delay()-Funktion enthalten ist: Eben damit nicht nur Zeit vertrödelt
wird, sondern zwischendurch etwas erledigt werden kann.
Was aber weiter unklar ist, warum ein Aufruf einer nicht implementierten
Funktion in manchen Fällen das Programm außer Tritt bringt.
Mitlesa schrieb:
...
>> Du siehst damit dass <yieldCount> verändert wird (obwohl in der> delay() gewartet wird), und das wesentlich häufiger als das> delay es vermuten lässt.
Was bisher noch keiner gesagt hat:
Wodurch wird denn yield() aufgerufen?
Bis jetzt wurde nur gesagt, wozu es benutzt wird. Aber wie ist der
Aufrufmechanismus? Und wann und wodurch wird es aufgerufen?
Hugo schrieb:> Aber wie ist der> Aufrufmechanismus? Und wann und wodurch wird es aufgerufen?
Wenn yield() vom Benutzer eingeführt (implementiert), also in
seinen Sourcen vorhanden ist, wird es implizit vom Arduino
Framework aufgerufen, was aus meinem zitierten Beispiel auch
klar erkennbar ist. Einfach mal lesen und nachdenken ....
Mitlesa schrieb:> Hugo schrieb:>> Aber wie ist der>> Aufrufmechanismus? Und wann und wodurch wird es aufgerufen?>> Wenn yield() vom Benutzer eingeführt (implementiert), also in> seinen Sourcen vorhanden ist, wird es implizit vom Arduino> Framework aufgerufen, was aus meinem zitierten Beispiel auch> klar erkennbar ist. Einfach mal lesen und nachdenken ....
In deinem Beispiel ist zu sehen, dass es aufgerufen wird. Aber nicht
wodurch. Und wann und wie oft, ist auch nicht zu sehen. Was nützt mir
eine Funktion, in der ich z.B. eine Variable incrementieren kann, wenn
ich nicht weiss, in welchem Rhythmus bzw. in welchem Intervall die
Funktion aufgerufen wird? Verstehst du, auf was ich hinauswill? :-)
Hugo schrieb:> Aber nicht wodurch.
Wenn du es nicht siehst woher es aufgerufen wird dann wohl von
"irgendwoher" anders. Die Quelle des Aufrufs kann aber nicht
weit sein da das Arduino-System deutlich abgegrenzt ist.
Wenn du es ausprobierst siehst du dass es aufgerufen wird.
Hugo schrieb:> Was nützt mir> eine Funktion, in der ich z.B. eine Variable incrementieren kann, wenn> ich nicht weiss, in welchem Rhythmus bzw. in welchem Intervall die> Funktion aufgerufen wird?
Ich bin nicht dazu da alle Gedankengänge der Arduino-Schöpfer
zu erklären und zu rechtfertigen. Aber wie wäre es wenn du deine
Nöte bei der Arduino-Gemeinde (sieh dortiges Forum) auslebst?
Mitlesa schrieb:> Ich bin nicht dazu da alle Gedankengänge der Arduino-Schöpfer> zu erklären und zu rechtfertigen.
Sorry, wenn das falsch rüberkam. Ich will nicht, daß du dich für Arduino
"rechtfertigst".
Ich hatte nur den Eindruck gewonnen, daß du dich auskennst und deshalb
die Fragen gestellt, die mir unklar waren.
Hugo schrieb:> In deinem Beispiel ist zu sehen, dass es aufgerufen wird. Aber nicht> wodurch.
Das wurde doch oben gepostet.
Ich hab's hier nochmal markiert:
Sören schrieb:
1
voiddelay(unsignedlongms)
2
{
3
uint32_tstart=micros();
4
5
while(ms>0){
6
--->yield();<----
7
while(ms>0&&(micros()-start)>=1000){
8
ms--;
9
start+=1000;
10
}
11
}
12
}
> Und wann und wie oft, ist auch nicht zu sehen.
So oft, wie's eben geht.
> Was nützt mir eine Funktion, in der ich z.B. eine Variable incrementieren> kann, wenn ich nicht weiss, in welchem Rhythmus bzw. in welchem Intervall> die Funktion aufgerufen wird? Verstehst du, auf was ich hinauswill? :-)
Die Funktion ist nur da, damit während des Delays noch was sinnvolles
gemacht werden kann. Wenn du dafür keinen Anwendungsfall dafür, dann
lasse sie einfach leer. Ansonsten kannst du da auch im Hintergrund
Bitcoins minen oder so. Die interessieren sich nicht für irgendwelche
Intervalle.
Danke auch von mir. Ich glaube, das ist jetzt verstanden.
Die Ursache meines Problems ist mir aber immer noch nicht klar.
Wenn diese Funktion yield() nicht implementiert ist würde ich meinen,
dass dann auch nichts passiert. Diese Erfahrung hatte ich auch bisher
gemacht und die delay()-Funktion benutzt, ohne überhaupt von der
Möglicheit zu wissen, mithilfe von yield() zwischendurch Dinge zu
erledigen.
Und jetzt plötzlich muss ich diesen Aufruf auskommentieren, damit mein
Programm läuft. Und nein, nirgends in meinem Code verwende ich eine
yield()-Funktion.
Ich nehme mal an, dass die yield() in Arduino als leere weak-Funktion
implementiert ist. Eine als weak gekennzeichnete Funktion wird nur dann
benutzt, wenn in Deinem Code nirgends eine Funktion mit gleichem Namen
definiert ist.
Schreib doch mal in Deine myDelay() eine myYield() statt der yield() und
definiere eine eigene myYield() in Deinem Code. Innerhalb von myYield()
solltest Du eine Dummy-Aktion (Hochzählen einer volatile Variablen, Pin
togglen etc.) einbauen, damit diese nicht wegoptimiert wird.
Falls das Problem damit immer noch auftritt, könnte es sich ev. um ein
Stackproblem handeln. Immerhin hat Dein Tiny nicht wirklich viel RAM.
Dafür dürfte auch sprechen, dass der Hänger nur sporadisch auftritt und
nicht sofort beim ersten Durchlauf.
Das Auskommentieren von yield() sorgt für ein paar Bytes weniger auf dem
Stack innerhalb von delay(). Das würde allerdings auch bedeuten, dass Du
mit den Ressourcen maximal am Anschlag bist und mit jeder Erweiterung
des Programms ähnliche Probleme auftreten können.
Viele Grüße, Stefan
Aktuelle Arduino IDE kompiliert afaik mit LTO. Von daher sollte die
Funktion normal verschwinden, wenn in der weak Implementierung auch nix
drinne steht.
Habe das jetzt mal ausprobiert und in delay() eine Funktin myYield()
aufgerufen, die einen Zähler hochzählt. Das Ergebnis wird in der
Main-Schleife ausgegeben.
An der Ausgabe ist zu erkennen, dass myYield() tatsächlich aufgerufen
und der Zähler inkrementiert wird.
Das Programm läuft damit jetzt schon eine ganze Weile ohne hängen zu
bleiben.
Nach dem Compilieren wird folgendes angezeigt:
Globale Variablen verwenden 121 Bytes (23%) des dynamischen Speichers,
391 Bytes für lokale Variablen verbleiben.
Das sollte doch reichen. Natürlich weiß ich nicht, was der Arduino für
seine internen Strukturen benötig. Aber mit fast 400 Bytes ist doch
einiges zu machen.
Nur zum Spaß habe ich dann nochmal die originale delay()-Funktion
reinkommentiert. Das Programm bleibt wieder nach ein paar Sekunden
hängen.
Nein, in meinem eigenen Code gibt es kein yield().
Es ist auch keine Library eingebunden.
Leider ist das alles sehr undurchsichtig. Ich werde das aber erst mal
auf sich beruhen lassen. Ein Fix ist ja gefunden.