Forum: Mikrocontroller und Digitale Elektronik Sleep und Timer


von JT (Gast)


Lesenswert?

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!

von (prx) A. K. (prx)


Lesenswert?

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?

von JT (Gast)


Lesenswert?

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.

von avr (Gast)


Lesenswert?

Dann verwende eine einfache RTC mit Alarmfunktion.
Damit weckst du den Tiny über Interrupt.

Ne RTC braucht "sehr" wenig Strom;)

avr

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von JT (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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.

von JT (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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.

von JT (Gast)


Lesenswert?

Aaah, okay, so meinst du das. Danke! :D

von Stephan (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Purzel H. (hacky)


Lesenswert?

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.

von avr (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Ü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

von avr (Gast)


Lesenswert?

@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

von (prx) A. K. (prx)


Lesenswert?

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". ;-)

von register (Gast)


Lesenswert?

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.

von register (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

Weiss nicht obs das Problem ist, aber
> LDS    r28, 0x0
an RAM-Adresse 0 liegt R0.

von (prx) A. K. (prx)


Lesenswert?

register schrieb:

> liegt? Oder habe ich da was falsch verstanden?

Nö, das war ich, weil genau da der Seitenumbruch ist. ;-)

von (prx) A. K. (prx)


Lesenswert?

Deine RAM-Initialisierung sieht auch etwas seltsam auf. Am Ende anfangen 
und hochzählen?

von register (Gast)


Lesenswert?

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...

von register (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von register (Gast)


Lesenswert?

@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"?

von Karl H. (kbuchegg)


Lesenswert?

> .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
Noch kein Account? Hier anmelden.