Guten Morgen!
Ich habe einen tiny45 samt 32kHz Quarz, der in den power-down versetzt
wird, und anschließend mit einem pin-change interrupt aufgeweckt und
direkt in den idle mode versetzt wird.
Fuse ist auf Low-Frequency Crystal Oscillator gesetzt (Verhalten
unabhängig von startup time).
Der Quarz hat keine Lastkapazitäten (laut Datenblatt: Empfehlung
Quarzhersteller beachten: 16pF), läuft aber bei reset trotzdem
problemlos an (andere AVRs können das wohl auch ohne Lastkapazitäten
gemäß Spezifikation).
Initialisierung
1
GIMSK=1<<PCIE;
2
PCMSK=1<<PCINT2;
3
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
4
sei();
Aufwachen
1
ISR(PCINT0_vect)
2
{
3
GIMSK&=~(1<<PCIE);
4
set_sleep_mode(SLEEP_MODE_IDLE);
5
}
'Runterfahren in main endlos
1
sleep_mode();
2
3
if(irgendwas){
4
GIMSK|=(1<<PCIE);
5
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
6
}
Das Ganze funktioniert auch nach dem einschalten, aber anschließend
manuell ("irgendwas") nicht:
anschalten .. ok
power down .. ok
aufwecken durch pcint, pcint deaktivieren, sleep .. ok
irgendwas: pcint aktivieren, power down .. ok
aufwecken durch pcint .. error
So, warum wird der PCINT nicht mehr ausgeführt?
Ich habe irgendwo gelesen, dass Quarze eine lange Anschwingzeit haben.
Setze ich die Fuses auf +64 ms gibt es keinen Unterschied.
Ich habe ausserdem gelesen, dass man den AVR nicht direkt nach dem
Aufwecken wieder in einen sleep mode versetzen sollte, habe dazu aber
nichts genaueres gefunden.
Datenblatt tiny45
http://www.atmel.com/dyn/resources/prod_documents/doc2586.pdfhttp://www.mikrocontroller.net/articles/Sleep_Mode#Morgenmuffel
Vielleicht habt ihr ja einen Tipp parat..
Bastler wrote:
> Ich habe ausserdem gelesen, dass man den AVR nicht direkt nach dem> Aufwecken wieder in einen sleep mode versetzen sollte, habe dazu aber> nichts genaueres gefunden.
Das trifft auf AVRs zu, die mit schnellem internen Takt arbeiten und nur
den Timer2 mit 32KHz (asynchron) fahren. Was hier kaum der Fall sein
kann, weil der Tiny45 m.W. nur komplett auf 32KHz geschaltet werden
kann.
Als Test kannst du mal den Tiny mit einem externen Takt an Stelle des
Quarzes versorgen, um die Fehlerquelle Oszillator auszuschliessen.
Fehlende Lastkapazitäten können zu seltsamem Quarzverhalten führen. Und
für die Frage, inwieweit AVR Oszillatoren im LF mode tatsächlich interne
Lastkapazitäten haben, reicht ein Blick in den Hauptteil des Datasheets
nicht aus (Mega8:ja), dazu muss man auch hinten in den Errata-Teil
reinschauen (Mega8:ätsch!).
Dann schläft er wohl für immer friedlich mit deaktiviertem PCINT :-)
Ich hatte die Stelle sogar schon böse angestarrt, allerdings habe ich
irgendwie gedacht, dass set_sleep_mode den Rechner schlafen legt, und da
wäre interrupts ausschalten ja eher schlecht gewesen.
Danke jedenfalls.
Also besser so:
Ach so, den Tipp mit dem externen Takt habe ich auch befolgt; ich habe
mangels Funktionsgenerator einen DS3234 benutzt. Der spuckt auch in
Minimalbeschaltung 3276Hz aus (vcc, gnd, 32khz). Fehlerquelle
ausgeschlossen ;-)
Siehe Beispiel in:
http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
Entscheidend dabei ist:
- Es darf vor dem SLEEP Befehle nicht vorkommen, dass der den Prozessor
aufweckende Interrupt schon bearbeitet wird.
- Es darf daher im Assembly-Listing zwischen SEI und SLEEP kein weiterer
Befehl zu sehen sein.
Jain, da ist noch ne Fallgrube im GCC.
Die Funktion sleep_mode() ist nämlich Bullshit, da sie nicht
interruptfest ist.
Sie darf nie und auf keinen Fall verwendet werden !!!
Die einzig richtige Reihenfolge ist im sleep.h zu Anfang angegebene:
Andreas Kaiser wrote:
> Siehe Beispiel in:> http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
Der Satz "As this combined macro might cause race conditions in some
situations" ist ja wohl die glatte Untertreibung.
Er müßte richtig heißen.
"This combined macro definitively cause race conditions (sooner or
later)"
Peter
Peter Dannegger wrote:
> "This combined macro definitively cause race conditions (sooner or> later)"
Yep. Das war von ein Paar Jahren mal ein Thema, damals war ich über
dieses Unvermögen der alten Version gestolpert. Mein Alternative dazu
sah ein bischen anders aus als die daraufhin integrierte, weil mir diese
Zwangskopplung zweiter aufeinanderfolgender C Statements [sei() =>
sleep_cpu()] nicht behagt, lief aber auf das gleiche Ergebnis raus.
> reicht aus, wenn die if()-Bedingung des Beispiels nicht nötig ist.
Wenn ich mir das genau ansehe, nein.
Das sleep_disable() ist ja auch nicht interruptfest.
Richtig müßte es so sein:
1
cli();
2
sleep_enable();
3
sei();
4
sleep_cpu();
5
cli();
6
sleep_disable();
7
sei();
Wobei ich allerdings nicht den Sinn verstehe, warum man überhaupt die
Modebits und das Enablebit getrennt setzen sollte.
Wenn man einen Mode setzt, impliziert das doch auch, daß man schlafen
will.
Peter
Der Gedanke dahinter ist wohl, dass man bei abgeschaltetem SE sich nicht
versehentlich mal schlafen legt und damit stehen bleibt. Kann ich auch
nur begrenzt nachvollziehen, aber was soll's.
Bleibt in der Sache zu berücksichtigen, dass man sich nur schlafen legen
darf, wenn man sicher sein kann, dass das Event was einen aufwecken
soll, auch noch eintreffen wird. Und nicht schon gerade eben
eingetroffen ist.
Deshalb sieht sowas ohne dieses rein-raus mit SE oft ungefähr so aus wie
im Beispiel skizziert:
cli
wenn es überhaupt noch einen Grund gibt sich schlafen zu legen, dann
sei
sleep
sei
wobei ich dann aber meistens auf die WinAVR GCC Sleep Makros verzichte
und selber die entsprechenden Register manuell setze, produziert
expliziteren Source.
Wichtig ist das das PCIF in GFIR gelöscht wird nach dem Aktivieren des
PCINTs.
verkürzt (mit bestimmten Rahmenbedingungen) sieht es dann so aus:
Wenn man den pin-change-int erst einschaltet, wenn man sich schlafen
legt, dann sollte man sich schon sicher sein, dass der noch kommt und
nicht grad schon war. Bei Tastern ist das meist weniger ein Problem,
aber bei Ereignissen deren Abstand man absolut nicht vorhersagen kann,
ist das durchaus ein Thema (z.B. bei einem I2C-Slave, der sich
zwischendurch schlafen legt).
korrekt, es hängt aber von der Zielstellung ab. In meinem Fall benutze
ich den PCINT zum Aufwecken das AVRs aus dem Powerdown, das weitere
Prozessing des PCINT Pins erfolgt dann nach dem Aufwachen des AVRs.
Solange also der AVR nicht im Powerdown ist wird die am PCINT Pin
angeschlossene Hardware anders ausgewertet. Solange diese HW noch aktiv
ist wird auch nicht in den Powerdown gewechselt. Das ist meistens der
Fall da man öfters noch andere Peripherie wie zb. Timer/ICP mit diesem
Pin verknüpft hat und diese Peripherie wäre im Powerdown sowieso
deaktiviert.
Gruß Hagen
Hagen Re wrote:
> Wichtig ist das das PCIF in GFIR gelöscht wird nach dem Aktivieren des> PCINTs.
Ist aber kein Muß.
Man würde nur sofort wieder aufwachen und dann die Sleepsequenz nochmal
durchlaufen.
Ein Deadlock kann nicht passieren.
> verkürzt (mit bestimmten Rahmenbedingungen) sieht es dann so aus:>
1
>MCUCR=(1<<SE)|(1<<SM1);
2
>cli();// asm volatile("cli");
3
>GIMSK=(1<<PCIE);
4
>GIFR=(1<<PCIF);
5
>sei();// asm volatile("sei");
6
>sleep_cpu();// asm volatile("sleep");
7
>GIMSK=0;
8
>
Ja das sieht schön aus, direkte Zuweisungen statt umständliche
Veroderungen.
Und die Freigebe des Weckinterrupts unter Interruptsperre direkt vor
SEI,SLEEP, da kann ja nichts mehr schiefgehen.
Peter
Andreas Kaiser wrote:
> Wenn man den pin-change-int erst einschaltet, wenn man sich schlafen> legt, dann sollte man sich schon sicher sein, dass der noch kommt und> nicht grad schon war.
Stimmt.
Wenn der nächste PCI erst kommt, nachdem man auf den vorherigen reagiert
hat, dann gibts wieder einen Deadlock.
Also das PCIF nicht händisch löschen, sondern lieber nen 2.Schlafversuch
riskieren, das ist universeller.
Peter
>Also das PCIF nicht händisch löschen, sondern lieber nen 2.Schlafversuch>riskieren, das ist universeller.
Mag sein, aber bei meinem letzten Projekt (Firefly in CodeLib) musste
ich vor der Sleep Sequenz den PullUp am PCINT Pin de/aktivieren. Das
führt dann par Takte später zum Auslösen des PCINT, deshalb lösche ich
lieber das PCIF in GFIR.
Erschwerend kam hinzu das der AVR über den Watchdog eine grobe Wartezeit
verbringen musste. Ein doppelt Auslösen des PCINT veränderte also die
Gesamtwartezeiten des AVRs über den WDT. Mal dahingestellt das der WDT
nicht sehr genau ist, aber der vor dem Powerdown eingestellte WDT
Timeout sollte unabhängig doppelter (falscher) PCINTs eingehalten
werden. Das erklärt also warum ich lieber das PCIF manuell lösche wenn
ich vor dem aktivieren des PCINT auch noch die Pullups am zu
überwachenden Pin aktiviere.
Übrigens löche ich das PCIF auch innerhalb der PCINT ISR am Ende dieser
ISR sher häufig. Das hängt aber eben von der Zielsetzung ab. Ist der
PCINT nur zum Aufwecken des AVRs gedacht dann meine ich sollte man das
PCIF auch löschen.
Gruß Hagen
Super, das Löschen von PCIF hat auch mein nächstes Problem gleich
erschlagen!
Kann das sein, dass das Datenblatt (2586K–AVR–01/08) da Quatsch erzählt?
• Bit 5 – PCIF: Pin Change Interrupt Flag
When a logic change on any PCINT5:0 pin triggers an interrupt request,
PCIF becomes set (one).
.....
Alternatively, the flag can be cleared by writing a logical one to
it.
Ausserdem hätte ich gerne gewusst, warum Hagen Re den Interrupt nicht in
der ISR löscht, sondern danach.
Danke.
Bastler wrote:
> Alternatively, the flag can be cleared by writing a logical one to
Doch, das stimmt so.
Es gibt bei AVRs nur wenige Interrupts, bei denen es in der ISR nötig
ist, sie explizit zu löschen. Bei den meisten geschieht dies automatisch
entweder bereits im Rahmen des Aufrufs der ISR oder durch Zugriff in der
ISR auf ein Transferregister.
Hi, ich meinte den ersten Teil des Absatzes:
Wenn ein PCINT auftritt, müsste PCIF doch 0 (zero) werden !?
Danach habe ich Unverständliches gefaselt und meinte das Deaktivieren
des Interrupts durch GIMSK = 0;
Ich mache das bislang direkt in der PCINT-ISR. Allerdings gibt es ja nur
zwei Fälle:
In der ISR:
wenn der PCINT nur zum aufwachen benutzt wird, muss sleep nur einmal im
programm vorkommen.
Nach sleep:
flexibler. Der Befehl muss ggf. nach jedem sleep aufgerufen werden.
Bastler wrote:
> Wenn ein PCINT auftritt, müsste PCIF doch 0 (zero) werden !?
Wenn der Pinzustand wechselt, wird PCIF 1. Wenn die ISR aufgerufen wurd,
wird PCIF automatisch 0.
>Danach habe ich Unverständliches gefaselt und meinte das Deaktivieren>des Interrupts durch GIMSK = 0;
Du musst das nicht so machen wie ich es oben gezeigt habe und kannst
GIMSK auch wenn nötig in der PCINT-ISR löschen. In meinem Fall wurde der
PCINT benutzt um zb. am Pin PB2 durch einen 1Wire-Bootloader den AVR aus
dem Powerdown zu wecken und danach sofort in der PCINT-ISR den
Bootloader im AVR über den Reset Vektor aufzurufen. Dh. AVR geht in den
Powerdown, dann und nur dann darf er über zb. PB2 an dem der 1Wire
Bootloader zum PC hängt, aufgeweckt werden. Sobald der AVR nicht im
Powerdown ist sollte in meinem Projekt der PCINT des Bootloaders auf
keinen Fall reagieren. Du kannst dir ja in der CodeLib hier im Forum mal
mein FireFly (Glühwürmchen im Rotkohlglass) Projekt anschauen.
Wenn die Zielsetzung deines Projektes es erlaubt das GIMSK innerhalb der
ISR zu ändern dann solltest du das auch nutzen. Denn Code in ISRs ist
auf Grund der fehlenden Interruptprioritäten im AVR auch gleichzeitigt
geschützt. Wenn es möglich ist versuche ich generell die komplette
Programlogik, also die Statemachine, innerhalb von ISRs zu erledigen.
Das vereinfacht den Handler in Main(). Allerdings geht das meistens
nicht so einfach.
Es hängt also von der konkreten Zielstellung ab.
Beim PinChange sollte man eben par Punkte beachten:
1.) um unnötige doppelte Triggerungen zu vermeiden lösche ich in der ISR
das PCIF Flag nochmals explizit am Ende dieser ISR. Denn in der
Zischenzeicht könnte ja nochmals ein PinChange aufgetreten sein. Das
sollte man immer dann machen wenn der PinChange quasi nur zum Auslösen
einer anderen Aktion benötigt wird, also unabhängig davon ob mehrere
PinChange Events eintreffen soll nur das erste PinChange Event eine
Aktion auslösen.
2.) aktiviert man den PinChange dann sollte man auch das PCIF löschen.
Besonders wenn man zb. den PullUp am Pin vorherig eingeschaltet hat. Ich
habe die Erfahrung gemacht das in diesem Fall par takte nach der
Aktivierung des Pullups der PCINT auslösst.
Gruß Hagen