mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Sleep Modus arbeitet nicht


Autor: Norbert Hanebeck (nobbyh)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sascha Weber (sascha_w)
Datum:

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

was mir sonst noch aufgefallen ist
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
*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
ledon:
    ldi temp, 0x6F         ; TimerValueH =0
    sts TCNT1H, temp       ; in Register schreiben
    ldi temp, 0x0A         ; TimerValueL =0
    sts TCNT1L, temp       ; in Register schreiben
    inc count1             ; count1 um 1 erhöhen
    out PORTC, count1      ; in Register schreiben
    reti                   ; Rücksprung



Sascha

Autor: Norbert Hanebeck (nobbyh)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Norbert Hanebeck (nobbyh)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Norbert Hanebeck (nobbyh)

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

Zuviel Red Bull? ;-)

Autor: Sascha Weber (sascha_w)
Datum:

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

mit
out  SMCR,temp
geht's

Sascha

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht 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 ...
.macro  out_
.if (@0 <= 63)
  out  @0,@1
.else
  sts  @0,@1
.endif
.endmacro
.macro  in_
.if (@1 <= 63)
  in  @0,@1
.else
  lds  @0,@1
.endif
.endmacro
dann hast du keine Probleme

Sascha

Autor: Norbert Hanebeck (nobbyh)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Grrrr (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Norbert Hanebeck (nobbyh)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.