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.
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
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?
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
@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?
++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 ...
@ 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.
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
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
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