Forum: Projekte & Code AVR-GCC Power-Down Tutorial


von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

AVR-GCC Power-Down Tutorial:


1. Fallgrube 1:

Sehr häufig wird der Fehler gemacht, daß zuerst versucht wird, den 
Power-Down Modus zu implementieren ohne eine funktionierende 
Entprellroutine zu haben. Wenn der Power-Down Modus durch Tastendruck 
erfolgen soll, muß man zuerst eine Entprellroutine haben.

Man fängt den Hausbau ja auch nicht mit dem Dach zuerst an, sondern mit 
dem Fundament. Und für jede Aufgabe, die per Tastendruck erfolgen soll, 
ist nunmal eine einwandfrei funktionierende Tastenroutine das Fundament.

Anzeichen für eine ungenügende Entprellroutine können sein:
- man drückt die Taste und der MC geht (manchmal)  nicht in Power Down.
- er geht in Power-Down, wacht aber (manchmal) nie wieder auf.
- er wacht auf, obwohl man keine Taste gedrückt hat (Störimpuls).

Die hier verwende Entprellroutine wurde ja schon an mehreren Stellen 
erkärt:

Beitrag "Tasten entprellen - Bulletproof"
Beitrag "Universelle Tastenabfrage"
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=1801


2. Fallgrube 2:

Der AVR-GCC enthält leider das Macro sleep_mode(), welches sehr 
gefährlich ist, da es in den meisten Fällen unerwünschte Nebeneffekte 
hat.
Diese Macro greift nämlich nicht atomar auf das MCUCR zu, was in 
größeren Programmen Probleme geben kann und wird.
Außerdem folgt das SLEEP nicht direkt auf das SEI einer vorhergehenden 
atomaren Operation zum Aktivieren des Aufwachinterrupts.
Deshalb sollte man auf dieses Macro unbedingt verzichten!


3. Beispielprogramm:

Das Programm implementiert einen LED-Fader mit SW-PWM.
Getestet wurde es auf dem STK500 mit dem ATtiny13.
Die PWM, das Faden und die Tastenentprellung werden im Timerinterrupt 
gemacht. Dadurch kann man gut sehen, wann der MC im Power-Down ist und 
wann nicht.

Neben der bekannten Tastaturroutine wird auch ein Bitmacro werwendet, 
damit man einfacher Bitvariablen implementieren kann. Dadurch wird das 
Programm leserlicher.
Eine Erklärung findet man z.B. hier:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=67368


Zeile 42:

Zum Aufwachen wird der Pin-Change-Interrupt verwendet. Nach dem 
Aufwachen disabled er sich selber. Das wird häufig gemacht, damit die 
CPU nicht mit unnötigen Interrupts geflutet wird.


Zeile 103:

Hier ist erstmal das harmlosere Kommando, um in den Idle-Modus zu gehen.
Dabei ist nur zu beachten, daß Änderungen des Sleep-Mode grundsätzlich 
atomar sein sollten.
In diesem Beispiel ist es zwar nicht nötig, aber in größeren Programmen 
kann es durchaus sein, daß auch Interrupts das MCUCR-Register zugreifen. 
Z.B. ein Timerinterrupt setzt nach einem Timeout den Power-Down-Mode 
oder ein externer Interrupt schaltet die Flanke um.

Ehe man sich also die Karten legt, wenn das Programm größer wird und man 
nicht mehr an den Sleep-Mode denkt, ist es daher sicherer, gleich von 
Anfang an sauber zu programmieren und Seiteneffekte von vornherein zu 
vermeiden.


Zeile 112:

Hier kommt die wichtigste Funktion, wo man besonders sorgfältig arbeiten 
muß. Fehler hierbei können sich selten bemerkbar machen und sind daher 
äußerst schwer zu finden.

Die Verbraucher abzuschalten bedarf wohl keiner Erklärung.

Dann sollte man alle nicht zum Aufwachen benötigten Interrupts 
abschalten, warum?
Nun, sie können zwar nicht neu auslösen, wenn man im Power-Down ist, 
aber sie können just zum Zeitpunkt des Sleep anhängig sein und dann wird 
das Sleep nicht ausgeführt (bzw. nur für einen CPU-Takt).
Es wird ja nur die Takt-Domäne abgeschaltet, nicht aber die 
Interruptlogik.

Zum Schluß muß man natürlich noch den Aufwachinterrupt scharf machen und 
den Power-Down Mode setzen.
Und alle diese Aktionen müssen atomar erfolgen, damit nicht ein 
Interrupt davon was rückgängig macht (z.B. SW-PWM schaltet LED wieder 
ein usw.).
Nach einer atomaren Funktion (SEI-Befehl am Ende) wird der nächste 
Befehl immer direkt ausgeführt, d.h. das SLEEP erfolgt, ohne das ein 
Interrupt dazwischen funken kann.

Hier kann man sich jetzt schön die Gefährlichkeit des sleep_mode() 
Macros ansehen, einfach oben den Kommentar vor USE_SLEEP_MODE löschen 
und nichts geht mehr.

Nach dem Sleep werden dann alle benötigten Interrupts wieder 
eingeschaltet.


Zeile 141:

Hier wird geprüft, ob der entprellte Zustand der Aufwachtaste auch dem 
am Pin anliegenden Signal entspricht, also die Entprellung abgeschlossen 
ist. Da der Taster aber low-aktiv ist, das entprellte Bit aber 
high-aktiv, wird auf Ungleichheit getestet (Exklusiv-ODER).
Bevor das Entprellen nicht beendet ist, darf der Power-Down nicht 
ausgeführt werden, da ja die Entprellung über den Timerinterrupt erfolgt 
und der dann nicht mehr laufen würde.
Man sieht auch schön, wie die CPU kurz aufwacht, um das Loslassen nach 
dem Power-Down zu entprellen.
In der Praxis würde man dann die LED natürlich nicht einschalten.

Ist das Entprellen beendet, wird die Taste ausgewertet. Sie schaltet nun 
zwischen Wachen und Schlafen hin und her. Dafür wird eine Variable 
genommen, damit erneut versucht werden kann in den Power-Down zu gehen, 
wenn schon ein Pin-Change-Interrupt anhängig ist oder wenn die 
Entprellung feststellt, daß es nur ein Störimpuls war.


Zeile 146:

Hier wird jetzt endlich die Power-Down Funktion aufgerufen, solange die 
Variable wahr ist.


Peter Dannegger

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:

> Der AVR-GCC enthält leider das Macro sleep_mode(), welches sehr
> gefährlich ist, ...

Wenn du denkst, dass damit was nicht in Ordnung wäre, dann diskutier
das bitte auf der avr-libc-dev Mailingliste, damit es korrigiert
werden kann.  Davon, dass du hier irgendwo in der Versenkung ein
Tutorial postest, hat über kurz oder lang niemand mehr was.

Eigentlich sind für deine Ansprüche die separaten Teilstücke
sleep_enable(), sleep_cpu() udn sleep_disable() gedacht.  Wenn du
denkst, dass deren Dokumentation noch irgendwie unzureichend wäre
und/oder die ,Gefährlichkeit' des einfachen sleep_mode() nicht
ausreichend beschrieben wäre, dann würden wir uns über einen
entsprechenden Patch für die Dokumentation freuen.  Wenn dein
Englisch nicht super-duper sein sollte, ist das auch nicht tragisch,
das wird dann schon noch korrekturgelesen.

von Geprellter (Gast)


Lesenswert?

Er will halt seine Entprellroutine in der Lib sehen und sich dadurch 
unsterblich machen.

MfG

von Klaus (Gast)


Lesenswert?

so ein Quatsch @Geprellter...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Geprellter schrieb:
> Er will halt seine Entprellroutine in der Lib sehen und sich dadurch
> unsterblich machen.

Die Entprellroutine ist gut, ist aber halt nicht so elementar, dass
sie Gegenstand der Bibliothek selbst wäre.  Allerdings hätte ich nichts
dagegen einzuwenden, wenn er daraus ein komplettes Beispiel zimmern
würde, das dann hierher kommt:

http://www.nongnu.org/avr-libc/user-manual/group__demos.html

Setzt aber voraus, dass es außer dem Quelltext selbst eine Doxygen-
Doku dazu gibt.

von Fabian B. (fabs)


Lesenswert?

@peter: würdest du auch noch deine atomic.h veröffentlichen? Das 
Atomic-Block interessiert mich...

Gruß
Fabian

von Bernhard M. (boregard)


Lesenswert?

die atomic.h ist doch Bestandteil der GCC Installation..

Fabian B. schrieb:
> @peter: würdest du auch noch deine atomic.h veröffentlichen? Das
> Atomic-Block interessiert mich...
>
> Gruß
> Fabian

von ... .. (docean) Benutzerseite


Lesenswert?

>Neben der bekannten Tastaturroutine wird auch ein Bitmacro werwendet,
>damit man einfacher Bitvariablen implementieren kann. Dadurch wird das
>Programm leserlicher.

Davon halte ich persönlich nichts... verwirrt bloß und sorgt für noch 
mehr Chaos...

Ein Tut sollte immer so wenig nicht Lib-Fkt. verwenden wie möglich. 
(Meine Meinung)

Oder erklärst du erst wie man ein Wohnzimmer streicht und dann wie man 
das Haus baut...

Diese Macros sind nicht unbedingt für die Funktion des Tut/Beispiel 
notwendig, also weg damit...

Die Tasterentprelldingsbums ist natürlich notwendig sonst sieht man 
sonstwas...

von Fabian B. (fabs)


Lesenswert?

@Bernhard: stimmt...hatte ich in der Liste übersehen...

Gruß
Fabian

von Jörg E. (jackfritt)


Lesenswert?

Hallo Peter,

ich habe gerade deinen Code für meinen Atiny13 unter WINAVR mit

avr-gcc -Wall -Os -fpack-struct -fshort-enums -funsigned-char 
-funsigned-bitfields -mmcu=attiny13 -DF_CPU=8000000UL -MMD -MP 
-MF"main.d" -MT"main.d" -c -o"main.o" "../main.c"

compilieren wollen und folgende Fehlermeldung bekommen. Kannst du mir 
sagen was ich falsch mache ?

../main.c: In function 'get_key_press':
../main.c:87: error: 'for' loop initial declaration used outside C99 
mode
../main.c: In function 'go_idle':
../main.c:112: error: 'for' loop initial declaration used outside C99 
mode
../main.c: In function 'go_power_down':
../main.c:121: error: 'for' loop initial declaration used outside C99 
mode
make: *** [main.o] Error 1

Gruss,

Jörg

von (prx) A. K. (prx)


Lesenswert?

-std=c99

von Jörg E. (jackfritt)


Lesenswert?

Sorry doppelpost.

Danke @ A. K.
hat funktioniert.

Sollte ich das Standardmäßig bei all meinen Projekten setzen ?

Gruss,

Jörg

von Bernd (Gast)


Lesenswert?

Hallo Jörg,

-std=gnu99
Sagt dem Compiler, dass er die gnu99 erweiterung benutzen soll... kommt 
also darauf an ob in deinem Code derartiges vorkommt :-)

http://www.rn-wissen.de/index.php/Avr-gcc

BTW: kommst Du aus dem Bergischen ?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.