Hallo zusammen
Ich habe hier ein kleinen Programm geschrieben, bei dem eine LED immer
dann angehen soll, wenn die LDR am AD-Eingang einen hohen Wert liefert.
Zusätzlich versetze ich das Programm auch in den Power Save Modus um
Strom zu sparen. Der derzeit eingebaute 18Mhz Quarz wird später noch
durch einen 32kHz Quarz ersetzt um lange Schlafzeiten zu erhalten.
Vor Eintritt in den Power Save schalte ich den AD-Wandler aus und nach
eine Schlafzeit von 2s wird eine Flag gesetzt, die die INterrupts
deaktiviert, den AD-Wandler aktiviert und eine Messung durchführt. Je
nach Ergebnis soll dann die LED eingeschaltet und anschließen die
Interupts aktiviert werden.
Es hat alles eigentl ganz gut funktioniert, bevor ich das mit dem AD
ein- und ausschalten programmiert hab. Wisst ihr was ich da vllt falsch
gemacht hab?
1
intmain(void)
2
3
{
4
//initLCD_Timer2(); // Initialiserung LCD und Timer2 OVF
5
6
ASSR=(1<<AS2);// Timer2 asynchron takten
7
_delay_ms(1000);// Einschwingzeit des 18MHz Quarzes
Mir fehlt eine Beschreibung, was denn nicht geht. Außer, dass es vorher
funktioniert habe, finde ich darüber nichts. Somit lautet die
Fehlerbeschreibung wohl „funktioniert nicht“. Das ist etwas mager.
Irgendwie wäre es schon schön, zu wissen, nach welchem Fehler man suchen
soll.
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
Mach mal ein Loch in Deinen Kopf, damit wir reingucken können, an
welchen MC Du gerade denkst.
Bei den AVRs braucht der T2 im asynchronen Mode unbedingt den 32kHz
Quarz!
Müssen es denn unbedingt exakt 2s sein?
Für ungefähr 2s nimm doch einfach den Watchdoginterrupt.
Peter
okay sorry ^^ stimmt ein paar infos fehlen wirklich :)
also es handelt sich um einen ATmega88
Achso für Asynchronen Modus brauch man zwingen den 32khz? okay das
wusste ich nicht :)
noch ne frage dazu... wieso muss er eigentlich unbedingt asynchron
laufen?
also später möchte ich mit dem 32khz quarz eigentlich nur alle paar
minuten, vllt so 10min, eine Messung vornehmen. kann ich dafür auch den
watchdog verwenden? ich dachte bei dem watchdog wacht er dann aus und
beginnt das programm von neuem, also als ob ein reset ausgeführt wurde
oder bin ich da falsch informiert ^^
Ok ... also Loch reinbohr!
Problem ist dieses... die LDR geht nun garnicht mehr an. Davor hatte ich
die Abfrage der Werte außerhalb der if(GO=1) schleife, da ging
wenigstens die LED, jetzt wo sie innerhalb ist geht nichts mehr :( und
ich seh den fehler nicht!
Der Watchdog im Mega88 kann auch Interrupts auslösen und damit
aufwecken. Ein auf Interrupt konfigurierter Watchdog wird, wenn er beim
Interrupt nicht neu bedient wird, erst im zweiten Anlauf einen Reset
auslösen. Maximale Zeit ist beim Mega88 8 Sekunden.
oh ja ;) seh ich grad
das ja cool... das heißt er wacht alle 8 sekunden auf wenn WDIF gesetzt
wird, oder?
Das heißt nun ich baue meine ISR(POWER_SAVE_MODE) ganz normal auf und
der Watchdog ruft diese alle 8s auf, weil dann ja ein Interrupt eintritt
oder?
Ich muss dann in den nächsten 8 Sekunden den Watchdog resetten, damit er
nicht einen kompletten Reset des Systems hervorruft. Hab ich das so
richtig verstanden?
Den Reset kann ich dann in der Hauptschleife setzen oder auch in der
ISR, wenn ich z.b. will, das mein Programm erst nach 80s eine Aktion
ausführt, setze ich ihn die ISR damit er mir dort eine Variable bis 10
hochzählt, bis er mir dann in das eingentliche Hauptprogramm springt...
Das ist das Prinzip, ja. Die ISR für den Watchdog-Interrupt muss
allerdings ISR(WDT_vect) heißen, wenn ich das richtig sehe. Ich würde
in der ISR eine Variable herunterzählen (und WDIE setzen) -- sonst
nichts -- und im Hauptprogramm solange schlafen, bis die Variable 0 ist.
Dann messen und die Variable neu setzen.
Alex schrieb:> Ist WDIE das gleiche wie z.b. wdt_enable(WDTO_8S); ?
Nein. Im Datenblatt steht (in C und Asm) ein Beispiel, wie Du den
Watchdog beschreibst. Du musst eine bestimmte Sequenz einhalten. Die
dafür nötigen Registerbits stehen auch in diesem Datenblatt (Beispiel
kurz vor, Registerbits in Abschnitt 8.9.2).
Alex schrieb:> Das WDIW muss ich dann in der ISR setzen oder in der while(1)-Schleife> vom main-Programm?
Ich nehme an, Du meinst WDIE. Das solltest Du direkt in der
Interrupt-Routine neu setzen (mit der oben erwähnten Sequenz) und sonst
(außer dem Herunterzählen des Counters) nicht viel machen. Um so
schneller ist das Hauptprogramm wieder dran, das die meiste Zeit gleich
in den nächsten sleep() geht.
werden diese nicht über die wdt.h mit diesem befehl gesetzt? oder bin
ich da schief gewickelt?
und wdt_reset(); resettet doch soweit ich das richtig in der
header-datei nachvollziehen konnte den Watchdog?
aber das mit dem reset in der ISR werd ich gerne übernehmen :)
Das programm springt doch, nachdem es aufgeweckt wurde, sofort hinter
den sleep-befehl, als dahin, wo es zuvor schlafen gegangen ist oder? es
fängt ja nicht wieder von vorne an...
wdt_reset() setzt nur den Watchdog-Timer zurück. Es beeinflusst keine
Flags. Am besten, Du schaust Dir im Datenblatt die Beschreibung zu WDIE
an. Soweit ich mich erinnere, muss das bei jedem Watchdog-Interrupt neu
gesetzt werden, sonst löst der Watchdog beim nächsten Mal einen Reset
aus.
Nachdem aufgeweckt wurde, arbeitet das Programm zunächst einmal den
Interrupt ab. Wie immer beim Interrupt wird nach dessen Erledigung das
Hauptprogramm dort fortgesetzt, wo es unterbrochen wurde -- in diesem
Fall nach dem sleep(). Dort prüfst Du, ob der Zähler seinen Endwert
erreicht hat und, wenn nicht, gehst in den nächsten sleep().
So ich hab jetzt hier probehalber nen Code geschrieben, welcher die LED
für eine Sekunde zum Leuchten bringen soll, nachdem der IC aufgeweckt
wird.
Habe mal deine Vorschläge befolgt, allerdings leuchtet die LED nicht und
er scheint auch garnicht in den Sleep Mode zu gehen :( Vllt wisst ihr wo
mein Fehler noch ist... Hoff ich hab die FUnktion ausreichend
kommentiert!
1
intmain(void)
2
3
{
4
WDTCSR|=0x48;// setzt WDIE und WDE für Interrupt and System Reset Mode
5
6
wdt_enable(WDTO_4S);// Watchdog auf 4Sekunden bis zum Auslösen
7
8
sei();// Interrupts aktivieren
9
10
while(1)
11
12
{
13
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
14
sleep_mode();// in den Schlafmodus wechseln
15
16
WDTCSR|=(1<<WDIE);// WDT Interrupt Flag erneut setzen, damit kein Reset erfolgt
> WDTCSR |= 0x48; // setzt WDIE und WDE für Interrupt and System Reset Mode
Das ist so nicht erlaubt. Nur das WDIE Bit kann direkt gesetzt werden.
Die Änderung des WDE Bits muss eine strenge Sequenz einhalten, die in
wdt_enable() bzw. wdt_disable() vorexerziert wird.
Wenn ich nun aber wdt_enable(4s) setze... dann ist doch noch nicht
gesagt ob er jetzt im System Reset Mode oder Interrupt Mode oder ...
läuft? da wird doch das WDE-Bit garnicht gesetzt! Ich kann leider auch
mit diesen Assembler-Befehlen nicht viel anfangen! :(
wdt_enable ist in avr/wdt.h als Inline Assembler Makro definiert.
In dem wdt_enable Code werden das WDE Bit plus die Prescalerwerte (hier
passend zu WDTO_4S) gesetzt, d.h es wird der System Reset Mode
eingestellt. Inline Assembler ist nicht einfach lesbar, aber das Setzen
des WDE erkennt man.
Das WDIE Bit würde ich (bzw. habe es im Demoprogramm) danach dazuodern.
Das geht mit Standard-C ohne Inline Assembler.
Dein Atmega88-Programm kann ich erst heute abend auf meinem Board testen
(wenn es auf Attiny2313 übertragbar ist). Im Attiny2313 Datenblatt ist
in der Tabelle mit den verschiedenen Modi auch noch die WDTON Fuse
aufgeführt. Bei meinen Experimenten mit dem Attiny2313 war es allerdings
nicht nötig die WDTON Fuse zu programmieren.
okay... LED fängt nun nach 4s an zu leuchten... leuchtet aber dauerhaft!
hab nun WDTCSR |= 0x48; entfernt und nach wdt_enable(); noch WDTCSR |=
(1<<WDIE) gesetzt. Das sollte mir doch nun den Reset und Interrupt Mode
geben, oder?
Habe dann WDTCSR |= (1<<WDIE) nochmals nach dem Sleep Befehlt gesetzt,
damit das Bit nach dem Interrupt gleich wieder gesetzt wird!
Was ist daran noch falsch? Muss ich zufällig noch wdt_reset(); benutzen?
Hab grad das zweite WDTCSR |= (1<<WDIE) durch wdt_reset(); ersetzt und
nun leuchtet die LED nach 4s und bleibt schätzungsweise 5s an und ist
dann wieder für 4s aus... wieso das?
ich hab die delay auf 1000ms gestellt, also sollte die doch 1s leuchten
und anschließend wieder in den sleep-modus gehen. sollte die dann nicht
ausgehen, wenn er dort reingeht?
Nee. Der Sleep Modus ändert hier nix an den Ports. Wenn du Ports im
Sleep Modus abschalten willst, um Strom zu sparen, musst du das selbst
machen. Vor dem Aufruf des Sleep.
hab nun mal den Stromverbrauch meines ICS gemessen...
also laut dem multimeter verbraucht er lange nur 4uA, geht dann ganz
kurz auf 7mA hoch und wieder zurück auf 4uA...
Also den Sleep_Modus wird er wohl machen, nur das die LED 4-5s leuchtet
verstehe ich noch nicht ;(
Im Grunde ists doch so:
Initialisierung
.
Sleepmodus
.
WD
.
ISR -> GO=1
.
LED an, da GO=1
.
1s warten, dann GO=0
.
Sleepmodus
.
WD
.
ISR -> GO=1 und zusätzlich LED aus!!!
.
LED an, da GO=1
.
.
.
Das bedeutet eigentl sie müsste an gehen und sobald sie in den interrupt
gelangt ausgehen um dann danach sofort wieder anzugehen. so könnte doch
niemals eine auszeit von 4s auftreten oder?
Sorry wird immer länger hier ;P
hab jetzt alles hinbekommen soweit ;)
Die LED leuchtet kurz und hört dann wieder auf... jetzt nochmal
abschließend:
Mit WDTCSR |= (1<<WDIE) setz ich das Bit, damit der WD nach dem
2.Durchlauf keinen System Reset durchführt, stimmts?
Und mit wdt_reset(); kann ich den 4s Timer wieder auf anfang setzen
oder?