Hallo liebe Leute! Ich benutze für mein Projekt einen ATtiny45 running at 8Mhz, und möchte gerne, dass dieser alle 60s in den IDLE-Sleep-Mode versetzt wird um Strom zu sparen, und dann nach ca. 6h wieder aufwacht. Ich habe es mir nun so gedacht, dass ich einen 60s-Timer setze, und bei dessen Overflow Interrupt in der Handler-Routine den Sleep-Mode aktiviere. Gleichzeitig habe ich einen zweiten Timer laufen (ca. 6h), und bei dessen Overflow Interrupt wecke ich den uC wieder auf. Aber wie genau mache ich das? Oder eher einen ganz anderen Ansatz? Da der Quellcode bereits in Assembler ist, werden Codebeispiele eurerseits in Assembler bevorzugt. Danke im Voraus!
JT schrieb: > Ich habe es mir nun so gedacht, dass ich einen 60s-Timer setze, und bei > dessen Overflow Interrupt in der Handler-Routine den Sleep-Mode > aktiviere. Gleichzeitig habe ich einen zweiten Timer laufen (ca. 6h), > und bei dessen Overflow Interrupt wecke ich den uC wieder auf. Normalerweise will man kontrollieren, an welcher Stelle vom Programm es einpennt. Und es nicht dem Zufall überlassen. Schlafen legen tut man es sich folglich in der Mainloop, wenns sein muss assistiert von einem zyklischen Timer und entsprechendem Zähler. Aufwecken tut ein Interrupt. Ist der Stromverbrauch kritisch und/oder muss die Zeit genau sein?
Hey, ja also bei mir ist die Situation so, dass die Zeitpunkte relativ unkritisch sind. Die Hauptschleife soll halt laufen und laufen und laufen - eben für ungefähr 60s lang, und dann eingeschläfert werden. Dementsprechend an welcher Stelle in der Hauptschleife ist unwichtig, sondern die Zeit ausschlaggebend, wo doch diese nicht exakt sein muss. Ja, stromkritisch schon, da das Projekt irgendwann mit ner Knopfzelle betrieben wird.
Dann verwende eine einfache RTC mit Alarmfunktion. Damit weckst du den Tiny über Interrupt. Ne RTC braucht "sehr" wenig Strom;) avr
JT schrieb: > Ja, stromkritisch schon, da das Projekt irgendwann mit ner Knopfzelle > betrieben wird. Dann dürfte der "Idle" Sleep-Modus der falsche Weg sein, der frisst weiterhin zu viel für eine Knopfzelle. Der wohl sparsamste Schlafmodus, der noch von selber wieder aufwacht, ist Powerdown mit Watchdog-Timer im Interrupt-Modus.
avr schrieb: > Dann verwende eine einfache RTC mit Alarmfunktion. > Damit weckst du den Tiny über Interrupt. Der Tiny45 hat die immense Zahl von 8 Pins und 5-6 I/Os. Mit RTC und Weckfunktion sind es dann schon einmal 3 weniger.
A. K. schrieb: > Der wohl sparsamste Schlafmodus, der noch von selber wieder aufwacht, > ist Powerdown mit Watchdog-Timer im Interrupt-Modus. Oops. Kann der Tiny45 nicht. Vielleicht gehts aber auch mit Reset.
Hey, vielen Dank mit dem Tipp mit Power Down und Watchdog! Doch eine Sache: Nach Datasheet vom ATtiny45 gibt es selbst mit Prescaler ein Timeout bereits nach 8s?! Kann ich das irgendwie zu 6h machen?
JT schrieb: > Doch eine Sache: Nach Datasheet vom ATtiny45 gibt es selbst mit > Prescaler ein Timeout bereits nach 8s?! > Kann ich das irgendwie zu 6h machen? Aufwachen, hochzählen, wieder einschlafen. Die paar Mikrosekunden spielen keine Rolle.
Sorry, aber ich versteh deine letzte Antwort nicht. Wenn der eingeschlafen ist, dann weckt der Watchdog ihn wieder. Per Datasheet ist der Watchdog-Timeout auch mit Prescaler bei nur 8s. Ich möchte ihn gerne bei um die 6h haben. Wenn der eingeschlafen ist, kann ich doch nichts mehr hochzählen? Oder etwa doch?
JT schrieb: > Wenn der eingeschlafen ist, kann ich doch nichts mehr hochzählen? Bist du noch nie morgens aufgewacht, hast dich aber wieder rumgedreht und weitergeschlafen? Der wacht auf, stellt fest dass ihn der Watchdog aufgeweckt hat und folglich der RAM-Inhalt noch da ist, und zählt eine dortigen Variable hoch. Hat die 6h/8s=2700 erreicht, dann wärs mal wieder an der Zeit ein bisschen zu arbeiten. Andernfalls rumdrehen und wieder einschlafen Haken an den Tinys ist, das die per Watchdog nur über Reset wieder aufwachen, neuere Megas können den Watchdog auch für Interrupts verwenden. Manchen solcher Programme macht es aber nichts aus, stets wieder ab Reset loszulegen. Du musst dann nur nach dem Reset im MCUSR/MCUCSR nachsehen, ob es auch der Watchdog war, oder nicht vielmehr Powerup/Brownout, bei denen der Zähler nicht initialisiert ist.
Klar kann er; braucht aber entweder ein paar RAM-Zellen, die während des Schlafens gepuffert werden; oder ins Flash geschrieben. Also alle 8s einen Zähler hochzählen und wieder schlafen legen, bis (6h/8s) = 21.600, dann entgültig wieder aufwachen. Stephan.
Stephan schrieb: > Klar kann er; braucht aber entweder ein paar RAM-Zellen, die während des > Schlafens gepuffert werden; RAM-Inhalt geht beim Powerdown-Sleep nicht verloren. In C müsste man sie in die entsprechende Section legen um die Initialisierung zu verhindern, aber hier ist ja sowieso Assmebler gefragt. Nach Powerup/Brownout muss man die Variable frisch initialisieren. > (6h/8s) = 21.600 ähhmmm
A. K. schrieb: > n C müsste man sie > in die entsprechende Section legen um die Initialisierung zu verhindern, Man kann das Power-Down auch durch ein Reset verlassen. Aber üblicher Weise erfolgt das per Interrupt. Dann sind alle Variablen in ihrem alten Zustand, auch unter C. Peter
Peter Dannegger schrieb: > Aber üblicher Weise erfolgt das per Interrupt. Hast recht, ich hatte mich vom Schema im Datasheet irritieren lassen. Beim Mega88 geht bereits aus diesem Schema hervor, dass er den Watchdog auch für Interrupts nutzen kann. Im Schema vieler Tinys hingegen gibt es keinen Watchdog-Interrupt, aber das ist zumindest beim Tiny45 gelogen. In der Registerbeschreibung taucht der WDIE auf. Ältere wie Mega8 oder Tiny15 haben wirklich keinen Watchdog-Interrupt. Atmel hat bei copy-and-paste offenbar vergessen, das Bild zu überarbeiten.
Da der code schon is Assembler ist, darf man erwarten dass das Datenblatt gelesen wird. Sichwort Sleepmodi, moegliche Aufwachbedingungen... die Register und Peripherals sind ja bekannt.
Der zusätzliche Stromverbrauch für den Watchdog beträgt je nach VCC und Temperatur ca. 3 µA (mal bei 3 V gepeilt). (ohne sind 0,2 µA mit dem Tiny möglich). Ne RTC gibt es locker mit nur 0,3 µA. avr.
Überschlagsrechnung für Powerdown, mit Knopfzelle 220mAh: aktiv ~2,5mA für 1min auf 6h: 42µAh Watchdog 4µA für 6h: 24µAh => 66µAh = 830 Tage RTC+CPU 0.5µA für 6h: 3µAh => 45µAh = 1220 Tage Die RTC verlängert die Betriebszeit also um 46%, wird aber mit jedem zusätzlichen Verbraucher geringer. Demgegenüber der ursprüngliche Ansatz im Idle: Stromverbrauch idle ~0,7mA für 6h: 4200µAh = 13 Tage
@A.K. du hast nur 1 Minute alle 6 Stunden gerechnet aber es kommen noch die Aufwachphasen alle 8 Sekunden dazu. Aber die Hauptfrage ist ob im Wachzustand 8 MHz nötig sind;) 8 MHz -> 2,5 mA 1 MHz -> 0,6 mA avr
avr schrieb: > @A.K. du hast nur 1 Minute alle 6 Stunden gerechnet aber es kommen > noch die Aufwachphasen alle 8 Sekunden dazu. Stimmt. Also 1,027ms statt 1,000ms, wenn 80 Takte fürs "umdrehen". ;-)
Hi, ich habe schon die ganze Nacht dran gesessen den Fehler in meiner
Implementierung für das Hochzählen im RAM zu finden, aber ich tue es
einfach nicht! :S
Hier der Code:
reset:
; accummulating timeouts to reach a 6h timeout!
in r28,MCUSR ;MCU Control- und Statusregister lesen
sbrs r28,WDRF ;Prüfen ob Watchdog-Restart erfolgte
rjmp start ;nein: Normalstart
cbr r28,1<<WDRF ;Watchdog-Resetflag zurücksetzen
out MCUSR, r28
rjmp watchdogReset ;WDRStart
;until here it must be okay
;problematic part
watchdogReset:
LDS r28, 0x0
cpi r28, 2
BREQ start
inc r28
STS 0x0, r28
.DEF PWR_DOWN = R16
; preparing sleep
LDI PWR_DOWN, (1<<SE) | (1<<SM1) | (0<<SM0)
OUT MCUCR, PWR_DOWN
SLEEP
start:
clr _0
ldiw X, RAMTOP ;Clear RAM
ldi AL, 0 ;
st X+, _0 ;
dec AL ;
brne PC-2 ;
ldi r26, 0;
; outi OSCCAL, 172 ;Adjust OSCCAL if needed.
outi PORTB, 0b001101 ;Initalize Port B; PB3 to 1 for LED
outi DDRB, 0b011010 ;
outi PLLCSR, 0b00000110 ;Initialize TC1 in 250 kHz fast PWM mode.
outi TCCR1, 0b01100001 ;Connect TC1 to OC1A
outi GTCCR, 0b01100000 ;Connect TC1 to OC1B
outi OCR0A, 62 ;Initalize TC0 in 32 kHz interval timer.
outi TCCR0A, 0b00000010
outi TCCR0B, 0b00000010
outi TIMSK, (1<<OCIE0A)
;setting up Watchdog
cli
outi WDTCR, (1 << WDCE) | (1 << WDE) ;Watchdog enable
outi WDTCR, (1 << WDCE) | (1 << WDE) | (1 << WDP3) | (0 << WDP2) |
(0 << WDP1) | (1 << WDP0) ;Prescaler Set
wdr
sei
start_play:
inc r26;
ldiw Z, score*2
cli
clrw _Tmr
clr _TmrS
sei
pl_next:
outi PINB, 0b1000 ; toggle LED with each note
wdr ; reset watchdog counter
lpmw B, Z+
rcall drv_decay
cli
cpw _Tmr, B
sei
brcs PC-5
pl_note:
lpm CL, Z+
cpi CL, EoS
breq sleep_handler
mov AL, CL
rcall note_on
andi CL, en
breq pl_note
rjmp pl_next
sleep_handler:
.DEF PWR_DOWN = R16
cpi r26, 3 ;playing three times and then into sleep mode
BRNE start_play
; preparing sleep
LDI PWR_DOWN, (1<<SE) | (1<<SM1) | (0<<SM0)
OUT MCUCR, PWR_DOWN
ldi r26, 0 ; reset counter
outi PORTB, 0 ; turn off LED
ldi r28, 0 ; set watchdog-timeout counter to zero
STS 0x0, r28
SLEEP
######
- watchdog wird in reset vorbereitet
- bevor der uC eingeschlafen wird, wird der Counter-Wert "0" an die
Adresse "0x0" in der RAM geschrieben
- jedes Mal nach nem Reset wird erst mal geprüft, ob es auch ein
Watchdog-Reset war, wenn ja dann überprüfen durch Lesen der
Counter-Werte aus dem RAM, ob es auch schon bereits das zweite Mal. Wenn
ja, dann weiter; ansonsten wieder einschläfern und Counter
inkrementieren.
Das Problem:
Der Timeout ist immer noch nur 8s sekunden lang statt 16s.
Vorläufige Ergebnisse:
Die Sektion zur Überprüfung auf ein Watchdog-Reset ist korrekt. Das
Problem liegt irgendwo im Teil, der ausgeführt wenn ein Watchdog-Reset
ausgeführt wurde. Dort brancht er scheinbar immer zu start, ungeachtet
der Counter-Werte. Dies lässt mich wiederum darauf schließen, das
irgendwas bei mir mit dem Schreiben und Lesen von RAM nicht stimmt.
Hmm, nach Datasheet von Atmel http://www.atmel.com/dyn/resources/prod_documents/doc2586.pdf für Attiny45 steht auf Seite 49, dass mit WDP[3..0] 1001 der Timeout bei 8s liegt? Oder habe ich da was falsch verstanden?
register schrieb: > liegt? Oder habe ich da was falsch verstanden? Nö, das war ich, weil genau da der Seitenumbruch ist. ;-)
Deine RAM-Initialisierung sieht auch etwas seltsam auf. Am Ende anfangen und hochzählen?
Ja, das habe ich mir schon gedacht, dass es irgendwie an der memory location liegt, die ich ausgewählt habe. Aber ich habe es auch schon mit RAMTOP und RAMEND probiert, funktioniert auch nicht... :S Wo sollte ich es deiner Meinung am besten platzieren? Habe gar keine Erfahrung...
Und von der Syntax her und generellen Umgang mit RAM habe ich aber schon richtig gemacht? Oder muss man da noch iwelche Bits in iwelchen speziellen Registern setzen um RAM-Nutzung zu aktivieren oder so?
register schrieb: > Wo sollte ich es deiner Meinung am besten platzieren? Habe gar keine > Erfahrung... Wie man Variablem im RAM ganz offiziell anlegt sollte im AVR Tutorial stehen.
register schrieb: > Ja, das habe ich mir schon gedacht, dass es irgendwie an der memory > location liegt, die ich ausgewählt habe. Aber ich habe es auch schon mit > RAMTOP und RAMEND probiert, funktioniert auch nicht... :S > > Wo sollte ich es deiner Meinung am besten platzieren? Habe gar keine > Erfahrung... Warum überlässt du dann nicht ganz einfach dem Assembler, die 'Variable' im RAM anzuordnen? http://www.mikrocontroller.net/articles/AVR-Tutorial:_SRAM Das hätte dann auch den Vorteil, dass du dich nicht mit dem Code des MP3_Players, der ja wohl auch ein wenig SRAM brnötigt, um das vorhandene SRAM prügeln musst, sondern der Assembler ordnet das alles sauber im Speicher an.
Wenn du nicht riskieren willst, dass der Wachhund alle 8s deine Melodie unterbricht, dann solltest du ihn zwischendurch mal streicheln (WDR). Und ein letztes Mal direkt vor dem Einschlafen.
@kbuchegg
Meintest du mit deinem Post die Adressenzuweisung mit .DSEG zu machen?
Das habe ich jetzt gemacht:
.dseg
.org RAMTOP
Counter: .BYTE 1
reset:
; accummulating timeouts to reach a 6h timeout!
in r28,MCUSR ;MCU Control- und Statusregister lesen
sbrs r28,WDRF ;Prüfen ob Watchdog-Restart erfolgte
rjmp start ;nein: Normalstart
cbr r28,1<<WDRF ;Watchdog-Resetflag zurücksetzen
out MCUSR, r28
rjmp watchdogReset ;WDRStart
;until here it must be okay
;problematic part
watchdogReset:
LDS r28, Counter
cpi r28, 10
BREQ start
inc r28
STS Counter, r28
.DEF PWR_DOWN = R16
; preparing sleep
LDI PWR_DOWN, (1<<SE) | (1<<SM1) | (0<<SM0)
OUT MCUCR, PWR_DOWN
SLEEP
start:
clr _0
ldiw X, RAMTOP ;Clear RAM
ldi AL, 0 ;
st X+, _0 ;
dec AL ;
brne PC-2 ;
ldi r26, 0;
; outi OSCCAL, 172 ;Adjust OSCCAL if needed.
outi PORTB, 0b001101 ;Initalize Port B; PB3 to 1 for LED
outi DDRB, 0b011010 ;
outi PLLCSR, 0b00000110 ;Initialize TC1 in 250 kHz fast PWM mode.
outi TCCR1, 0b01100001 ;Connect TC1 to OC1A
outi GTCCR, 0b01100000 ;Connect TC1 to OC1B
outi OCR0A, 62 ;Initalize TC0 in 32 kHz interval timer.
outi TCCR0A, 0b00000010
outi TCCR0B, 0b00000010
outi TIMSK, (1<<OCIE0A)
;setting up Watchdog
cli
outi WDTCR, (1 << WDCE) | (1 << WDE) ;Watchdog enable
outi WDTCR, (1 << WDCE) | (1 << WDE) | (1 << WDP3) | (0 << WDP2) |
(0 << WDP1) | (1 << WDP0) ;Prescaler Set
wdr
sei
start_play:
inc r26;
ldiw Z, score*2
cli
clrw _Tmr
clr _TmrS
sei
pl_next:
outi PINB, 0b1000 ; toggle LED with each note
wdr ; reset watchdog counter
lpmw B, Z+
rcall drv_decay
cli
cpw _Tmr, B
sei
brcs PC-5
pl_note:
lpm CL, Z+
cpi CL, EoS
breq sleep_handler
mov AL, CL
rcall note_on
andi CL, en
breq pl_note
rjmp pl_next
sleep_handler:
.DEF PWR_DOWN = R16
cpi r26, 3 ;playing three times and then into sleep mode
BRNE start_play
; preparing sleep
LDI PWR_DOWN, (1<<SE) | (1<<SM1) | (0<<SM0)
OUT MCUCR, PWR_DOWN
ldi r26, 0 ; reset counter
outi PORTB, 0 ; turn off LED
ldi r28, 0 ; set watchdog-timeout counter to zero
STS Counter, r28
SLEEP
Aber funktionieren tut es immer noch nicht... Immer noch etwas nicht
ganz korrekt?
@A.K.
Streicheln tue ich ihn immer in pl_next...
Meintest du mit "offiziellem Ablegen von Variablen im RAM" auch ".dseg"
oder was anderes?
Und ich verstehe nicht ganz was du meinst mit "am Ende anfangen und dann
hochzählen"?
> .dseg > .org RAMTOP > Counter: .BYTE 1 und das steht so im Tutorial? Speziell der Teil mit RAMTOP? Schon mal was vom Stack gehört und das der vom RAMTOP abwärts wächst? Machs ganz einfach so wie im Tutorial gezeigt. Was spricht dagegen, dass du deine 'Variablen' am Code-Ende anordnest? Was spricht dagegen, dass du den Assembler selbst entscheiden lässt, wo er im SRAM den SPeicher auftreibt, ohne dass du ihm mit .ORG etwas vorzuschreiben versuchst? Genau. Gar nichts.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.