Es geht um Ein- und Ausstieg in den Power-Down (Standby wäre auch ok)
bei einem AVR Tiny 1627, ist aus der Series 2.
Der soll aus dem Power-Down entweder auf Tastendruck (funktioniert) oder
nach einer Zeitspanne wieder zurückkehren und ein paar Dinge
verarbeiten.
Für das zyklische will ich die RTC bzw deren Mode "Periodic Interrupt
Timer (PIT)" verwenden.
PIT verwende ich schon im aktiven Betrieb, nur soll der vor dem Einstieg
in den Power down stark verlangsamt werden. Meine Erwartungshaltung
wäre, dass beim ersten Interrupt des PIT die CPU wieder aufwacht.
Tut sie nicht: manchmal läuft sie ungebremst über sleep_cpu() hinweg,
manchmal kommt die nie wieder da raus. Es sei denn, ich löse einen Port
i/o Interrupt aus.
Initialisierung des Sleep Controllers:
1
SLPCTRL.CTRLA=SLPCTRL_SMODE1_bm;
Und hier der zyklische Power-Down, der definitiv nicht funktioniert:
Ist etwas überladen, um den Fehler auf die Spur zu kommen. Leider
vergeblich.
Vielleicht hat das schon mal jemand hinbekommen und kann mir einen Tipp
geben.
Oder klarstellen, so gehts gar nicht, was mich wundern würde :-)
Nur mal vom Draufgucken:
Warum gibt es die Warteschleife auf RTC_CTRLBUSY_bm nur in einem der
beiden Zweige?
Hast du auch eine ISR für den PIT-Interrupt? Solltest du ja ;-) Dann
würde ich aber auch konsequenterweise das RTC_PI_bm da drin gleich
löschen.
Habe den PIT schon benutzt, aber noch nicht, um aus dem Sleep
aufzuwachen.
Achso, gibt es einen Grund, nicht gleich sleep_mode() zu nehmen?
Ich würde mal vermuten, daß auch bei den neueren AVRs Sleep nur in
Zusammenarbeit mit einem Interrupthandler funktioniert.
Und bei den klassischen AVRs muß man vor dem nächsten Sleep noch warten,
bis die RTC-Register mit dem RTC-Takt synchronisiert sind, sonst wird
der Interrupt mehrfach getriggert. Dafür sind die xx_Update_Busy Bits
gedacht.
Das sleep_disable(); ist akademischer Furz (vollkommen überflüssig) und
kann weg. Einmal sleep_enable(); ins Init und gut.
Peter D. schrieb:> Jörg W. schrieb:>> Achso, gibt es einen Grund, nicht gleich sleep_mode() zu nehmen?>> Ja, (Race Conditions).
Welche können das realistisch sein? Vor dem Sleep hat man normalerweise
eh nur die Peripherie aktiv, die einen daraus aufwecken soll. Auch sehe
ich nicht, welchen Schaden ein zwischen den drei Teilschritten
eintreffender Interrupt anrichten sollte.
Also ja, man kann das sleep_enable() auch ganz am Anfang machen und dann
aktiv lassen (und nur noch sleep_cpu() benutzen), aber ich sehe keinen
wirklichen Sinn drin, statt eines sleep_mode() die drei darin
enthaltenen Teilschritte selbst einzeln hinzumeißeln.
Jörg W. schrieb:> Welche können das realistisch sein?
Z.B. wenn der Aufwachinterrupt sich selbst disabled und dann genau vor
das sleep_cpu(); reinhaut.
Es gibt doch schon reichlich Threads mit dem "sleep forever Problem".
Wie schon gesagt, das sleep_disable(); hat absolut keine sinnvolle
Funktion.
Jedes Programm verhält sich exakt gleich, wenn man es wegläßt. Es ist
also nur vergeudeter Flash und CPU-Zyklen.
Veit D. schrieb:> Hallo,> habe die Serie nicht zur Hand, denke jedoch du musst noch "Standby" der> jeweiligen Einheit aktivieren damit sie weiterläuft.
Ja richtig, ist gewährleistet. Init des Timers:
1
voidtimer_setup(void)/*Periodic Interrupt Timer (PIT) wird hier benutzt */
Peter D. schrieb:> Ich würde mal vermuten, daß auch bei den neueren AVRs Sleep nur in> Zusammenarbeit mit einem Interrupthandler funktioniert.
Es gibt eine ISR (RTC_PIT_vect).
> Das sleep_disable(); ist akademischer Furz (vollkommen überflüssig) und> kann weg. Einmal sleep_enable(); ins Init und gut.
Danke für den Hinweis, dann muss ich mir den Sleep-Controller im Detail
anschauen. Hatte die Sequenz nur irgendwo abgekupfert.
Jörg W. schrieb:> Warum gibt es die Warteschleife auf RTC_CTRLBUSY_bm nur in einem der> beiden Zweige?
Hast Recht, das ist inkonsequent. Im Datenblatt steht, man muss das
busy-Flag beim Schreiben auf die PIT-Register abpollen. Müsste man dann
immer tun. Wobei ich bisher ohne auskam.
Georg M. schrieb:> Beispielcode: ...
Herzlichen Dank, schaue ich mir genau an.
Peter D. schrieb:> Z.B. wenn der Aufwachinterrupt sich selbst disabled und dann genau vor> das sleep_cpu(); reinhaut.
Das hat aber nicht viel mit sleep_mode() zu tun, außer dass das Fenster
für diese race condition damit etwas größer wird. Am Ende ist das ein
Designfehler.
Jörg W. schrieb:> Das hat aber nicht viel mit sleep_mode() zu tun
Doch, daß hat sehr viel damit zu tun. Schließlich ist sogar in der
"sleep.h" ein Beispielcode für die korrekte Lösung angegeben.
Jörg W. schrieb:> außer dass das Fenster> für diese race condition damit etwas größer wird.
Ein "Sleep forever" würde ich nicht gerade als nur "etwas größer"
bezeichnen.
Aber auch, wenn die Mainloop um einen zusätzlichen Timerzyklus
verzögert, kann das schon Auswirkungen haben.
In einer BA eines MP3 Players habe ich mal gelesen: "Die Zeitanzeige
kann bei häufiger Benutzung etwas nachgehen". Schönes Eingeständnis für
Unfähigkeit.
Peter D. schrieb:> Wulf D. schrieb:>> Es gibt eine ISR (RTC_PIT_vect).>> Und warum zeigst Du sie dann nicht?>> Fehlersuche geht immer erheblich besser bei vollständigem Code.
Ist hier, hab ich für das Problem als irrelevant angesehen. Aber ja,
hätte erwähnen müssen, dass es eine ISR gibt.
1
ISR(RTC_PIT_vect)// ca 12ms
2
{
3
ticker++;
4
if(intr_count==TIMEIRQ)// ca 1/10 sec Takt
5
{
6
task|=(1<<task100ms);
7
if(blink==2)PORTA.OUTTGL=LED;// LED toggeln
8
intr_count=0;//making intr_count=0 to repeat the count
9
if(intr_c10==9)
10
{// ca 1 sec Takt
11
intr_c10=0;
12
++sec;
13
sleepsec++;
14
if(sleepsec==10)
15
{
16
task|=(1<<tasksleep);
17
sleepsec=0;
18
}
19
if(blink==1)PORTA.OUTTGL=LED;// LED toggeln
20
anz|=(1<<anzjetzt);
21
if(klickrep>2&&klickinc<256)klickinc<<=1;
22
elseif(klickrep<2&&klickinc>1)klickinc>>=1;
23
klickrep=0;
24
}
25
elseintr_c10++;
26
}
27
elseintr_count++;
28
RTC.PITINTFLAGS=RTC_PI_bm;
29
}
Habe Georgs Democode adaptiert und der Power-Down wird damit jedesmal
zuverlässig betreten und verlassen!
Jetzt funktioniert zwar mein restlicher Code nicht mehr, aber das ist
sicher irgend ein Randeffekt mit dem RTC. Finde ich sicher selbst raus,
komme da aber erst morgen zu.
Wäre interessant woran es gelegen hat. Georgs Code ist sehr kompakt,
werde ich noch rausfinden.
Hier die Adaption. Init:
Wulf D. schrieb:> Jetzt funktioniert zwar mein restlicher Code nicht mehr, aber das ist> sicher irgend ein Randeffekt mit dem RTC.
Nee, hatte nichts mit der RTC zu tun, die läuft.
Es war der ADC, der nach dem Power-Down neu gestartet werden muss.
Der ist so konfiguriert, dass nach erfolgter Konvertierung der beim
Setzen seines Eingangs-Multiplexer automatisch eine neue Konvertierung
startet.
Da passiert nach einem Power-Down nichts mehr. Muss neu angestoßen
werden.
Also zum Code oben noch eine Zeile hinzugefügt:
1
ADC0.MUXPOS=uadc[adcindex].muxpos;
Die rechte Seite ist einfach eine Portnummer.
Vielen Dank für die Tipps, hat das Problem in kürzester Zeit gelöst!
Schaue morgen mal, was der entscheidende Punkt war.
Wulf D. schrieb:> PIT verwende ich schon im aktiven Betrieb
Beim laufenden Mikrocontroller braucht man den asynchronen PIT
eigentlich nicht. Stattdessen kann ein TCB eingesetzt werden.