Anbei mal ein Beispiel, wie man sauber und unkompliziert den
Power-Down-Mode implementiert, um einen MC ab und wieder einzuschalten.
Gerade Anfänger tun sich damit schwer, weil sie versuchen, die Funktion
des Gerätes mit dem Sleepmode zu vermanschen. Und das ist nicht nur
höllisch kompliziert, sondern geht auch oft schief.
Der Spruch "Divide et impera" (teile die Aufgaben auf und Du herrscht
über das Programm) kann besser nicht bewiesen werden.
Mein Trick ist daher, sich erstmal um den Sleep-Mode überhaupt nicht zu
kümmern, sondern sich nur auf die reine Funktion zu konzentrieren.
Anbei mal als Beispiel ein einfaches Lauflicht mit verschiedenen Modi
über ne Tabelle gesteuert. Die Tabelle läßt sich einfach editieren und
erweitern. Für die Experten habe ich als Aufgabe gelassen, die Tabelle
in den Flash zu verlagern.
Mit kurzem Tastendruck wird zwischen den Modi umgeschaltet. Mit langem
Tastendruck wird an und ausgeschaltet, der aktuelle Modus bleibt dabei
unverändert.
Die Tastenroutine dürfte bekannt sein:
Beitrag "Universelle Tastenabfrage"
Die Ausschaltfunktion wird über die Flagvariable "stop" gesteuert. Ist
sie wahr, werden die LEDs abgeschaltet und die Knight Rider Routine
übersprungen. Die CPU läuft aber noch weiter.
Der Code steht in "Without Sleep.zip".
Nachdem also alle Funktionen komplett und getestet sind, gehts ans
Schlafen programmieren.
Durch die Flagvariable ist das sehr einfach. Solange sie wahr ist, wird
ständig versucht, zu schlafen:
1 | if( stop ){
|
2 | try_sleep();
|
Vor dem Sleep natürlich nicht vergessen, den Aufwachinterrupt zu
enablen:
1 | PCICR = 1<<PCICR_ENABLE; // awake interrupt on
|
2 | sleep_cpu();
|
3 | PCICR = 0;
|
Der Aufwachinterrupt hat nichts weiter zu tun, kann also leer bleiben:
1 | EMPTY_INTERRUPT( PCINT_vect ); // wake up from power down
|
Den Schlafmodus und den Aufwachpin kann man schon im Init vorbereiten:
1 | PCMSK_REG = 1<<PCINT_BIT; // prepare awake pin
|
2 | set_sleep_mode( SLEEP_MODE_PWR_DOWN); // prepare sleep
|
3 | sleep_enable();
|
Und nun kommt das schwierigste. Die Tastenentprellung braucht einen
Timer, aber der läuft ja im Power-Down nicht. Daher darf man erst
schlafen, wenn das Entprellen abgeschlossen ist. Bei meiner Routine ist
das der Fall, wenn die Aufwachtaste losgelassen ist (= high) und das
entprellte Bit = low.
Daher muß noch dieser Test erfolgen, bevor man schläft:
1 | uint8_t get_key_busy( uint8_t key_mask )
|
2 | {
|
3 | return (~KEY_PIN | key_state) & key_mask; // until key released
|
4 | }
|
5 |
|
6 |
|
7 | void try_sleep()
|
8 | {
|
9 | if( get_key_busy( 1<<KEY0 ))
|
10 | return; // still busy
|
11 | LED_PORT = 0xFF; // all LEDs off
|
12 | PCICR = 1<<PCICR_ENABLE; // awake interrupt on
|
13 | sleep_cpu();
|
14 | PCICR = 0;
|
15 | }
|
Alternativ kann man aber auch den Watchdoginterrupt oder den RTC-Timer
(T2 + Uhrenquarz) zum Entprellen nehmen, die laufen ja auch im
Power-Down weiter.
So und das wars dann auch schon.
Zum Debuggen ist noch diese Zeile nach dem Sleep drin:
1 | LED_PORT = 0x00; // all LEDs on (for debug only)
|
Bleibt die CPU also nicht im Sleep stehen, leuchten alle LEDs.
Damit sieht man sehr schön, wie beim Drücken die CPU aufwacht. Ist der
Druck nur kurz, geht sie wieder schlafen.
Diese Zeile kann also nach dem Test entfernt werden.
Zusätzlich habe ich noch einen Timer eingebaut. Wird 1min keine Taste
gedrückt, wird "stop" auf wahr gesetzt und damit der Power Down Mode
aktiv.
Der komplette Code steht in "Knightrider.zip".
Peter