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.