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 :-)
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.
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.
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.
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.
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.
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.
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.
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.