Forum: Mikrocontroller und Digitale Elektronik Sleep Modus arbeitet nicht


von Norbert H. (nobbyh)


Lesenswert?

Hallo,

habe das folgende Programm in eine ATMEGA88 geschrieben,
und habe den Eindruck, dass die Sleep-Funktion nicht ausgeführt wird.

;-----------------------------------------------------------------
;
; Name:         Sleep-Funktion-Test1_M88-16MHz.asm
; Title:        Wait for Timer1 Interupt and blink LED
;
; Version:      0.1
; Last updated: 2010.09.18 by nobby
; Target:       ATMEGA88
;
;-----------------------------------------------------------------
;
;
; RX/TX: MAX232
; INT0 and INT1: not used
;
; Output RS-232: not used
;
;
; History:
; ========
;
; 18.09.2010
; Software aus anderem Progamm kopiert
;
; 18.09.2010
; Software geändert.
; CLKDivider zur Reduzierung des Taktes.
; Wichtig: Umschaltung muss innerhalb von 4 Takten erfolgen.
; Interupt darf währenddessen nicht auftreten bzw. muss
; deaktiviert sein.
;
; Aufgabe:
; Timer1 erzeugt ein Zeitintervall von 152 Sekunden: OK
; bis zum Interupt soll die CPU schlafen: nicht OK
; Anzeige des Interupt über Umschaltung der LED an PC0: OK
; Anzeige des Sleep-Modus über Umschaltung der LED an PB0: nicht OK
;
;
;-----------------------------------------------------------------


.include "M88def.inc"

.equ fq          = 16000000    ; Quarzfrequenz
.equ prescaler   = $05         ; Teilerfaktor 1024
.def temp        = r16
.def temp1       = r18
.def count1      = r17


    rjmp main         ; Reset Handler
    reti              ; IRQ0 Handler
    reti              ; IRQ1 Handler
    reti              ; PCINT0 Handler
    reti              ; PCINT1 Handler
    reti              ; PCINT2 Handler
    reti              ; Watchdog Timer Handler
    reti              ; Timer2 Compare A Handler
    reti              ; Timer2 Compare B Handler
    reti              ; Timer2 Overflow Handler
    reti              ; Timer1 Capture Handler
    reti              ; Timer1 Compare A Handler
    reti              ; Timer1 Compare B Handler
    rjmp ledon        ; Timer1 Overflow Handler
    reti              ; Timer0 Compare A Handler
    reti              ; Timer0 Compare B Handler
    reti              ; Timer0 Overflow Handler
    reti              ; SPI Transfer Complete Handler
    reti              ; USART, RX Complete Handler
    reti              ; USART, UDR Empty Handler
    reti              ; USART, TX Complete Handler
    reti              ; Analog Comparator Handler
    reti              ; 2-wire Serial Interface Handler
    reti              ; Store Programm Memory Ready Handler

main:
    ; Initialisieren
    ldi temp, high(RAMEND) ; Stackpointer H initialisieren
    out SPH, temp          ; in Register schreiben
    ldi temp, low(RAMEND)  ; Stackpointer L initialisieren
    out SPL, temp          ; in Register schreiben

    ldi temp, 0x01         ; Port B0 als Ausgang
    out DDRB, temp         ; in Register schreiben

    ldi temp, 0x01         ; Port C0 als Ausgang
    out DDRC, temp         ; in Register schreiben

    ldi temp, 0x00         ; Port C0 low = LED on
    out PORTC, temp        ; in Register schreiben

    ; Externe Interrupts aktivieren
    ldi temp, (1<<TOIE1)   ; Overflow Interupt Timer1 aktivieren
    sts TIMSK1, temp       ; in Register schreiben

    ; Clock Divider setzen
    ldi temp, $80          ; Daten laden (CLKPCE = 1)
    sts CLKPR, temp        ; in Clock Prescale Register schreiben
    ldi temp, $06          ; Daten laden (CLKPS2+CLKPS1 = 1 = /64)
    sts CLKPR, temp        ; in Clock Prescale Register schreiben

    ; Timer1 initialisieren
    ldi temp, (1<<TOV1)    ; Timerflag zurücksetzen
    out TIFR1, temp        ; in Register schreiben
    ldi temp, 0x6F         ; TimerValueH =0
    sts TCNT1H, temp       ; in Register schreiben
    ldi temp, 0x0A         ; TimerValueL =0
    sts TCNT1L, temp       ; in Register schreiben
    ldi temp, prescaler    ; Prescaler setzen
    sts TCCR1B, temp       ; in Register schreiben
    sei                    ; Interupts aktivieren

    ;count1 null setzen
    ldi count1, $00        ; Lade temp mit 0
    ldi temp1, $00         ; setze temp1 =0

    ; Power Save Modi aktivieren
    ldi temp, $E7          ; PRTWI PRTIM2  PRTIM0  -  PRTIM1 PRSPI 
PRUSART0 PRADC
    sts PRR, temp          ;   1      1       1    0     0     1      1 
1
                    ; PRTWI    = Power Reduction TWI
                    ; PRTIM2   = Power Reduction Timer2
                    ; PRTIM0   = Power Reduction Timer0
                    ;  -       = not used
                    ; PRTIM1   = Power Reduction Timer1
                    ; PRSPI    = Power Serial Peripheral Interface (no 
RS232)
                    ; PRUSART0 = Power Reduction USART0
                    ; PRADC    = Power Reduction ADC

    ldi temp, $0D          ; Lade Power Save code
    sts SMCR, temp         ; in Sleep Mode Control Register

loop:  sleep              ; Sleep Modus aktivieren
    inc temp1             ; temp1 um 1 erhöhen
    out PORTB, temp1      ; Wert auf PortD schreiben
    rjmp loop             ; Warten auf Interupt

ledon:  cli                ; Interupts deaktivieren
    ldi temp, $00          ; Temp mit 0 laden
    sts TCCR1B, temp       ; in Register schreiben (Timer1 anhalten)
    ldi temp, 0x6F         ; TimerValueH =0
    sts TCNT1H, temp       ; in Register schreiben
    ldi temp, 0x0A         ; TimerValueL =0
    sts TCNT1L, temp       ; in Register schreiben
    ldi temp, prescaler    ; Prescaler setzen
    sts TCCR1B, temp       ; in Register schreiben
    ldi temp, (1<<TOV1)    ; Timerflag zurücksetzen
    out TIFR1, temp        ; in Register schreiben
    inc count1             ; count1 um 1 erhöhen
    out PORTC, count1      ; in Register schreiben
    sei                    ; Interupts aktivieren
    reti                   ; Rücksprung
;-----------------------------------------------------------------

Die Anzeige des Interupt funktioniert einwandfrei.
Alle 152 Sekunden schaltet die LED um.
Das Gleiche müsste meiner Meinung nach doch auch an der
anderen LED passieren.
Das Programm wird nach der Intialisierung bis zum Sleep-Befehl
ausgeführt und hält dort an.
Nach dem Interupt und der Ausführung der Interuptrountine sollte das 
Programm hinter dem Sleep Befehl fortfahren.
Das bedeutet, dass die zweite LED auch im 152s Rhythmus schalten müsste.
Dem ist aber nicht so. Diese LED leuchtet permanent.

Mache ich einen Gedankenfehler? Oder hat das Programm einen Fehler?

Hab das Manual in diesem bereich schon zigmal durchgelesen, finde aber 
keinen Fehler.

von Sascha W. (sascha_w)


Lesenswert?

du verwendest den STANDBY-MODE lt. Datenblatt sollte der sich doch nur 
von Timer2 unterbrechen lassen!?

was mir sonst noch aufgefallen ist
1
ledon:  cli                ; Interupts deaktivieren
2
    ldi temp, $00          ; Temp mit 0 laden
3
    sts TCCR1B, temp       ; in Register schreiben (Timer1 anhalten)
4
    ldi temp, 0x6F         ; TimerValueH =0
5
    sts TCNT1H, temp       ; in Register schreiben
6
    ldi temp, 0x0A         ; TimerValueL =0
7
    sts TCNT1L, temp       ; in Register schreiben
8
    ldi temp, prescaler    ; Prescaler setzen
9
    sts TCCR1B, temp       ; in Register schreiben
10
    ldi temp, (1<<TOV1)    ; Timerflag zurücksetzen
11
    out TIFR1, temp        ; in Register schreiben
12
    inc count1             ; count1 um 1 erhöhen
13
    out PORTC, count1      ; in Register schreiben
14
    sei                    ; Interupts aktivieren
15
    reti                   ; Rücksprung
*cli/sei kannst du dir schenken, innerhalb der ISR sind die INT's 
gesperrt
*um den Counter zu laden brauchst du ihn nicht anzuhalten, da er bei dir 
eh nur alle 1024 Takte die ISR aufgerufen wird
*das TOVx-Flag wird durch das Aufrufen der ISR gelöscht
bleibt also
1
ledon:
2
    ldi temp, 0x6F         ; TimerValueH =0
3
    sts TCNT1H, temp       ; in Register schreiben
4
    ldi temp, 0x0A         ; TimerValueL =0
5
    sts TCNT1L, temp       ; in Register schreiben
6
    inc count1             ; count1 um 1 erhöhen
7
    out PORTC, count1      ; in Register schreiben
8
    reti                   ; Rücksprung



Sascha

von Norbert H. (nobbyh)


Lesenswert?

Sascha Weber schrieb:
> du verwendest den STANDBY-MODE lt. Datenblatt sollte der sich doch nur
>
> von Timer2 unterbrechen lassen!?

@ Sascha,
vielen Dank für Deine Hinweise.
Das ich jetzt den Standby-Mode genommen habe ist Zufall. Ich habe auch
Power Save Mode und auch andere versucht. Gleiche Erscheinung.

Aber wenn ich der oben genannten Aussage folge, würde dass nicht 
bedeuten,
dass die CPU nie mehr aufgeweckt wird?
Wieso läuft denn der Timer1 weiter?

von Sascha W. (sascha_w)


Lesenswert?

Norbert Hanebeck schrieb:
> Aber wenn ich der oben genannten Aussage folge, würde dass nicht
> bedeuten,
> dass die CPU nie mehr aufgeweckt wird?
richtig
> Wieso läuft denn der Timer1 weiter?
weil der AVR nicht in den SLEEP geht.

Wie wird dein AVR getaktet (intern oder extern)?
für einige SLEEP-MODEs steht im Datenblatt das die nur funktionieren 
wenn die Takterzeugung mit externem Quarz/Resonator erfolgt (siehe 
Kapitel 9)

Deine LED aus der Hauptschleife sollte dann eigentlich nicht dauernd 
leuchten, sondern so schnell umschalten das es nur so aus sieht.

Sascha

von Norbert H. (nobbyh)


Lesenswert?

@Sascha,

Der Atmega88 ist mit einem externen 16-MHz Quarz bestückt.
Habe zusätzlich noch eine Zeitschleife von 5 Sekunden
eingefügt.

loop:  sleep              ; Sleep Modus aktivieren
    inc temp1             ; temp1 um 1 erhöhen
    out PORTB, temp1      ; Wert auf PortB schreiben
    rcall delay           ; 5 Sekunden Delay
    rjmp loop             ; Warten auf Interupt

Die LED an PortB blinkt nun im 5 Sekunden Rhythmus
während die LED an PortC im 152 Sekunden Rhytmus blinkt.

Aber warum geht die CPU nicht in den Sleep Modus?

von Falk B. (falk)


Lesenswert?

@Norbert Hanebeck (nobbyh)

>Aber warum geht die CPU nicht in den Sleep Modus?

Zuviel Red Bull? ;-)

von Sascha W. (sascha_w)


Lesenswert?

hab's mal im Simulator getestet ...
mit
1
sts  SMCR,temp
wird das SMCR nicht verändert und bleibt bei 0

mit
1
out  SMCR,temp
geht's

Sascha

von Sascha W. (sascha_w)


Lesenswert?

++EDIT
da ist mir gerade noch eingefallen warum das nicht gehen kann. Zwischen 
der I/O-Adresse und der SRAM-Adresse hast du einen Offset von 32!
In den includes sind die Definitionen für den I/O-Bereich 0..63 auf eben 
diese I/O-Adressen ausgelegt. Bei allen Registern oberhalb von 63 ist 
der Offset schon eingerechnet. Willst du also ein Register aus dem mit 
IN/OUT erreichbaren Bereich mit STS/LDS ansprechen so musst du noch 32 
addieren.

oder verwende Macros ...
1
.macro  out_
2
.if (@0 <= 63)
3
  out  @0,@1
4
.else
5
  sts  @0,@1
6
.endif
7
.endmacro
8
.macro  in_
9
.if (@1 <= 63)
10
  in  @0,@1
11
.else
12
  lds  @0,@1
13
.endif
14
.endmacro
dann hast du keine Probleme

Sascha

von Norbert H. (nobbyh)


Lesenswert?

@ Sascha,

vielen Dank für die freundliche Unterstützung.

Ich denke, ich werde jetzt weiterkommen. Manchmal hat man ja das 
sogenannte Brett vorm Kopf.
Hatte schon diverse Dinge ausprobiert, aber nie den Sleep-Modus 
aktivieren können. Irgendwann ist ein Punkt erreicht, an dem man ja 
langsam an sich selbst zweifelt.
Aber völlig klar. Wenn man das entsprechende Register nicht mit den 
notwendigen Daten beschreibt, dann reagiert die CPU auch nicht wie sie 
soll.

Wie hast du denn im Debug-Modus festgestellt, dass die Daten nicht in 
SMCR
geschrieben werden?

Viele Grüsse
Norbert

PS: Ich wollte das Register nicht unbedingt über STS/LDS bearbeiten.

von Grrrr (Gast)


Lesenswert?

Norbert Hanebeck schrieb:
> PS: Ich wollte das Register nicht unbedingt über STS/LDS bearbeiten.

Leider hast Du bei Adressen oberhalb von hex 3F keine Wahl.
Die Befehle IN und OUT akzeptieren keine Adressen von mehr als hex 3F

von Sascha W. (sascha_w)


Lesenswert?

Norbert Hanebeck schrieb:
> Wie hast du denn im Debug-Modus festgestellt, dass die Daten nicht in
> SMCR
> geschrieben werden?
im Simulator des AVR-Studio kann man im IO-Window doch den Inhalt aller 
Register betrachten.

Grrrr (Gast) schrieb:
>Norbert Hanebeck schrieb:
>> PS: Ich wollte das Register nicht unbedingt über STS/LDS bearbeiten.
                               -----
>Leider hast Du bei Adressen oberhalb von hex 3F keine Wahl.
SMCR-Addr =0x33
>Die Befehle IN und OUT akzeptieren keine Adressen von mehr als hex 3F
wer hat das Gegenteil behauptet?

Sascha

von Norbert H. (nobbyh)


Lesenswert?

Sascha Weber schrieb:
> Wie hast du denn im Debug-Modus festgestellt, dass die Daten nicht in
>
>> SMCR
>
>> geschrieben werden?
>
> im Simulator des AVR-Studio kann man im IO-Window doch den Inhalt aller
>
> Register betrachten.
>

Danke, hab's gefunden. Fenster war nicht automatisch eingeblendet.

Sascha Weber schrieb:
> Die Befehle IN und OUT akzeptieren keine Adressen von mehr als hex 3F
>
> wer hat das Gegenteil behauptet?
Niemand

Nochmals recht herzlichen Dank für die Hilfe.

Gruss
Norbert

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.