Forum: Mikrocontroller und Digitale Elektronik Sleep-Funktion / Power Save


von NobbyH (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Forengemeinde,

habe folgende Problematik:
Möchte einen batteriebetriebenen Sensor bauen.
Da dieser nur alle 3 Minuten senden soll, möchte ich den Strom
in der inaktiven Zeit möglichst minimieren.
Dazu habe ich mir die Sleep-Funktion ausgesucht.

Zu Testzwecken habe ich das anhängende Programm geschrieben:
Dies funktioniert bei mir allerdings nur bedingt:
Befehl:  ldi   temp, (1<<SE|0<<SM1|0<<SM0) (IDLE MODUS)
         out   SMCR, temp
Das versetzt die CPU in den IDLE MODE und funktioniert auch soweit.
Damit erreiche ich einen Strom von ungefähr 170µA.
Es werden an PD7 Impulse erzeugt.

Dies ist mir aber noch nicht gering genug.
Wenn  ich nun den obigen Befehl durch:
Befehl:  ldi   temp, (1<<SE|1<<SM1|1<<SM0)  (POWER SAVE MODUS)
         out   SMCR, temp
ersetze, reduziert sich der Strom erheblich. Leider erhalte ich dann
aber keine Impulse an PD7 mehr, was darauf hin deutet, dass du CPU
steht bzw. kein Interupt mehr erkannt wird.

Die Hardware: ein M48 in Minimalbeschaltung mit 8MHz internem Takt und 
32,768kHz Quarz für Timer 2.

Kann mir jemand sagen warum der Power Save Modus nicht funktioniert?

Gruß
NobbyH

von Sascha W. (sascha-w)


Lesenswert?

Hallo,

also eigentlich sollte es gehen, habe den PWR-Save selbst schon beim m48 
mit 32kHz Quarz verwendet und kann in deiner Initialisierung auch keinen 
Unterschied zu der bei mir verwendeten erkennen.
Mal am Quarz gemessen ob er schwingt?

Sascha

von NobbyH (Gast)


Lesenswert?

Hallo Sascha,

ob der Quarz richtig schwingt bin ich mir nicht wirklich sicher.
Ich messe am Quarz mit dem Scope nur eine geringe Amplitude (ca. 0,8Vpp)
Mich wundert allerdings, dass der Idle Mode ja zu funktionieren scheint.

Keiner einen Tipp oder ein Beispielprogramm zum Test.

Gruß
NobbyH

von Sascha W. (sascha-w)


Lesenswert?

so mal ein Test,

LED sollte im Sekundentakt blinken
1
.include  "m48def.inc"
2
3
;Register
4
5
.def  isnull    = r6
6
.def  SRegSave  = r11
7
.def  TempU1    = r17
8
9
.cseg
10
.org  0
11
12
RESET:
13
I_Reset:  rjmp  Start
14
I_Int0:    reti
15
I_Int1:    reti      
16
I_PCINT0:  reti
17
I_PCINT1:  reti
18
I_PCINT2:  reti
19
I_WDT:    reti
20
I_Timer2_CompA:  reti
21
I_Timer2_CompB:  reti
22
I_Timer2_OV:  rjmp  T2_OV  ;reti
23
I_Timer1_Capt:  reti
24
I_Timer1_CompA:  reti
25
I_Timer1_CompB:  reti
26
I_Timer1_OV:  reti  
27
I_Timer0_CompA:  reti
28
I_Timer0_CompB:  reti
29
I_Timer0_OV:  reti
30
I_SPI:    reti
31
I_UART_Rx:  reti
32
I_UART_UDRE:  reti
33
I_UART_Tx:  reti
34
I_ADC:    reti
35
I_EERdy:  reti
36
I_ANA_CMP:  reti
37
I_I2C:    reti
38
I_SPM:    reti
39
40
41
42
;_______ I S R ________
43
;############## T I M E R ##############################
44
T2_OV:    ;1Hz
45
    in  SRegsave,SReg
46
    sbi  PIND,7    ;PD7 invertieren
47
48
    out  SReg,SRegsave
49
  reti
50
51
;------------------------------------------------------------------------------------------
52
Start:    ldi  TempU1,low(RAMEND)
53
    out  SPL,TempU1
54
    ldi  TempU1,high(RAMEND)
55
    out  SPH,TempU1
56
57
    clr  isnull
58
;CONFIG
59
  ;PORT'S
60
    ldi  TempU1,0x0ff
61
    out  PORTB,TempU1
62
    ldi  TempU1,0x00
63
    out  DDRB,TempU1
64
65
    ldi  TempU1,0xff
66
    out  PORTC,TempU1
67
    ldi  TempU1,0x00
68
    out  DDRC,TempU1
69
70
    ldi  TempU1,0xff
71
    out  PORTD,TempU1
72
    ldi  TempU1,0x80
73
    out  DDRD,TempU1
74
75
76
  ;Timer 2  ;Uhrzeit
77
    ldi  tempu1,(1<<AS2)
78
    sts  ASSR,tempu1
79
    
80
    ldi  tempu1,(5<<CS20)
81
    sts  TCCR2B,tempu1
82
    ldi  tempu1,(1<<TOIE2)
83
    sts  TIMSK2,tempu1
84
  
85
86
    sei
87
88
89
gosleep:  ;Power
90
    ldi  tempu1,(1<<PRTWI)|(1<<PRTIM0)|(1<<PRTIM1)|(1<<PRSPI)|(1<<PRUSART0)|(1<<PRADC)
91
    sts  PRR,tempu1
92
    ;Sleepmode
93
    ldi  tempu1,(3<<SM0)|(1<<SE)    ;Power-Save
94
    out  SMCR,tempu1
95
96
dosleep:  sleep
97
    nop
98
    nop
99
    rjmp  dosleep

hast du bei dir evl. den Watchdog am laufen? Da deine LED erst nach 8s 
umschaltet würde sich bei einem zwischenzeitlichen Restet keine Änderung 
an der LED ergeben.

Sascha

von NobbyH (Gast)


Lesenswert?

Hallo Sascha,

vielen Dank für Bereitsstellung des Programms.
Habe es mal reinprogrammiert. Es funktioniert leider auch nicht.

Dann habe ich mal folgende Zeile geändert
ldi  tempu1,(0<<SM0)|(1<<SE)    ;Idle Mode

und schon blinkt die LED im 1 Sekundentakt.

Das heißt ja auch, dass der Quarz laufen müsste.

Bei Verwendung des Power Save Mode reduziert sich die Stromaufnahme
deutlich.
Daraus folgere ich, dass die CPU bei Power Save nicht mehr aus dem Sleep
Modus aufwacht.

Habe es mit zwei unterschiedlichen Boards (einmal mit ATMEGA48PA und 
einmal mit ATMEGA48V) getestet und beide zeigen das gleiche Ergebnis.

Watchdog ist übrigens nicht aktiviert.

Habe echt keinen Lösungsansatz.

Gruß
NobbyH

von Purzel H. (hacky)


Lesenswert?

Gemaess Datenblatt kann beim Mega48  der Powerdown verlassen werden mit
-Timer2, falls 32kHz an Timer2
-Pinchange, Int0, Int1
-TWI adress match

von Sascha W. (sascha-w)


Lesenswert?

NobbyH schrieb:
> Habe echt keinen Lösungsansatz.
da gehen mir auch die Ideen aus. Der einzige Unterschied zu meiner 
Schaltung ist, das ich einen m88 verwende, aber außer der Speichergröße 
sollte da ja kein Unterschied sein.

Aktiviere doch mal den INT0 oder INT1 und lasse eine andere LED Togglen 
wenn er aus dem Sleep kommt - mal sehen ob er da wenigstens aufwacht.
Wie hast du den Takt per Fuse eingestellt? Bei mir läuft er mit 8MHz.

Sascha

von Thomas E. (thomase)


Lesenswert?

NobbyH schrieb:
> Die Hardware: ein M48 in Minimalbeschaltung mit 8MHz internem Takt und
> 32,768kHz Quarz für Timer 2.
> Kann mir jemand sagen warum der Power Save Modus nicht funktioniert?
Das hat mich auch schon an den Rand des Wahnsinns getrieben.

Setz mal den Takt auf 1MHz runter.
Das ist ein Phänomen, das ich schon mehrfach mit verschiedenen 
Schaltungen beobachtet habe. 8MHz, 2x AA bzw. AAA. Also 3V Nennspannung. 
Mit Netzteil sieht es auch nicht anders aus.

Bei "normalen" 48ern spinnt der CPU-Takt beim Aufwachen durch den 
Timer2-Interrupt. Eine Uhr schafft dann die Minute locker in 20 
Sekunden. Beim Atmega48a bzw. pa, kommt er gar nicht zurück. Mit anderen 
Controllern, 168, 168pa genauso.
Schaltet man den CPU-Takt auf 1MHz läuft alles wie erwartet.

Lässt sich auch immer wieder reproduzieren.

48v habe ich noch nie benutzt. Würde ich aber vom Verhalten her wie die 
a oder pa einordnen.

Ich habe da keine Erklärung für, denn die Controller sollen ab 2,7V mit 
bis zu 10MHz laufen. Ist einfach ein Erfahrungswert, der sich immer 
wieder gezeigt hat.

Vielleicht hat ja jemand eine Lösung dafür. Aber bitte nicht 
"100nF-Kondensatoren vorhanden?". Meine heisst bislang 1 MHz. Leider, 
denn manchmal dürfte es ein bisschen mehr sein.

mfg.

von Peter D. (peda)


Lesenswert?

Warum lest ihr denn nicht das Datenblatt.
Da steht klip und klar, das im asynchronen Mode die Flags erst mit dem 
Timertakt aktualisiert werden.

Beim Aufwachen hat man mehrere Möglichkeiten:

1. Man mache ein Dummy-Write auf ein Timerregister und wartet bis das 
zugehörige Busy-Bit gelöscht ist.

2. Man wartet solange, bis ein 32kHz Takt vorbei ist, also 32µs.

3. Man benutzt abwechselnd Compare-A und -B zum Aufwachen und disabled 
den jeweils anderen.


Peter

von Sascha W. (sascha-w)


Lesenswert?

hmm, da hab ich gleich noch mal geschaut.

Also er läuft bei mir mit RC 8MHz und gesetzter CKDIV8 (Fuse Low =0x62) 
also 1MHz am Ende. Bei Ub=3V (2xAAA)

Sascha

von Sascha W. (sascha-w)


Lesenswert?

Peter Dannegger schrieb:
> Warum lest ihr denn nicht das Datenblatt.
> Da steht klip und klar, das im asynchronen Mode die Flags erst mit dem
> Timertakt aktualisiert werden.
>
> Beim Aufwachen hat man mehrere Möglichkeiten:
>
> 1. Man mache ein Dummy-Write auf ein Timerregister und wartet bis das
> zugehörige Busy-Bit gelöscht ist.
>
> 2. Man wartet solange, bis ein 32kHz Takt vorbei ist, also 32µs.
>
> 3. Man benutzt abwechselnd Compare-A und -B zum Aufwachen und disabled
> den jeweils anderen.
>
>
> Peter

interessant, dann wird wohl hier erst mal das Problem sein, das die Zeit 
nach dem Beschreiben von TCCR2B (Takt für Timer ein) bis zum sleep schon 
nicht ausreicht, und so der Timer gar nicht erst läuft.
Fällt in meinem org. Program nicht auf, da der Sleep erst nach einigen 
Sekunden aufgerufen wird. Und die ISR ist auch länger.

Sascha

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> Da steht klip und klar, das im asynchronen Mode die Flags erst mit dem
> Timertakt aktualisiert werden.
Ja nee is klar.
Ist ja schon peinlich. Jaja, lesen bildet, alles lesen noch mehr.
Besten Dank.

mfg.

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> 3. Man benutzt abwechselnd Compare-A und -B zum Aufwachen und disabled
> den jeweils anderen.
Danach wird das Flag des gerade eingeschalteten gesetzt. Das muß wieder 
gelöscht werden. Dann klappt es auch mit 8 MHz.
1
int main(void)
2
{
3
  TCCR2A |= (1 << WGM21);
4
  TCCR2B |= (1 << CS20);
5
  TIMSK2 |= (1 << OCIE2A);
6
  OCR2A = 31;
7
  OCR2B = 31;
8
  ASSR |= (1 << AS2);
9
  DDRB |= (1 << 2);
10
  DDRD |= (1 << 6);
11
  
12
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
13
  sleep_enable();    
14
15
  sei();
16
17
  while(1)
18
  {
19
    sleep_cpu();        
20
  }
21
}
22
23
ISR(TIMER2_COMPA_vect)
24
{
25
  TIMSK2 ^= ((1 << OCIE2A) | (1 << OCIE2B));
26
  TIFR2 |= TIMSK2;
27
    
28
  PORTD ^= (1 << 6);
29
}
30
ISR(TIMER2_COMPB_vect, ISR_ALIASOF(TIMER2_COMPA_vect));

mfg.

von NobbyH (Gast)


Lesenswert?

Hi,

erst mal vielen Dank an die dochnun zahlreicheren Hilfestellungen.

@Sascha,
danke für Deine Unterstützung. Habe einmal den CKDIV8 Fuse aktiviert.
Dann funktioniert es. Danach habe ich die Clock per Programm durch 8 
geteilt. Auch dies funktioniert. Als ich dann aber die Clock durch
32 geteilt habe funktioniert es wieder nicht mehr. Daraus ist zu 
folgern,
dass die CKDIV8 Teilung ein Zufallstreffer ist und nicht die korrekt 
Vorgehensweise.
Ich habe die ganze Zeit über vermutet, dass es ein Timingproblem die 
Ursache ist.

Somit solten wir dies Ansätze von Peter Dannegger und Thomas Eckammn 
weiter verfolgen.

@Thomas Eckmann,
freundlicherweise hast Du eine Lösung in Form eines C-Programms 
angeboten. Da ich C noch nicht beherrsche wäre es nett, wenn Du mir das 
uin Assembler übertragen könntest.
Nicht, das ich hier alles vorgekaut haben möchte, aber um es besser 
nachvollziehen zu können.
Ich habe übrigens schon etliche Stunden in den letzten 2 Wochen mit der 
Suche nach der Lösung sowohl im Manual als auch im Internet verbracht.

Nochmal vielen Dank an Alle.

Gruß
NobbyH

von Peter D. (peda)


Lesenswert?

Thomas Eckmann schrieb:
> Danach wird das Flag des gerade eingeschalteten gesetzt. Das muß wieder
> gelöscht werden.

Nö, das macht der Interrupthandler.
Man muß natürlich beide Interrupts auf unterschiedliche Zeiten setzen, 
also:
1
  OCR2A = 63;
2
  OCR2B = 31;
Ansonsten hat man doch wieder das gleiche Problem bei zu schnellem 
CPU-Takt.


Peter

von Carsten R. (kaffeetante)


Lesenswert?

Vieleicht ist ja fogendes relevant :)

Seite 151 des Manuals vom Mega48

ATmega48/88/168
18.9 Asynchronous operation of Timer/Counter2

"When entering Power-save or ADC Noise Reduction mode after having 
written to TCNT2, OCR2x, or TCCR2x, the user must wait until the written 
register has been updated if Timer/Counter2 is used to wake up the 
device. Otherwise, the MCU will enter sleep mode before the changes are 
effective. This is particularly important if any of the Output Compare2 
interrupt is used to wake up the device, since the Output Compare 
function is disabled during writing to OCR2x or TCNT2. If the write 
cycle is not finished, and the MCU enters sleep mode before the 
corresponding OCR2xUB bit returns to zero, the device will never receive 
a compare match interrupt, and the MCU will not wake up"

Es steht aber auch in den Manuals der anderen Chips ähnlich, nur auf 
anderen Seitenzahlen.

Prüfe vor dem sleep mal ob die verschiedenen update busy flags im 
ASSR-Register allesamt weg sind. Falls nicht warte bis sie weg sind. Das 
Update der Register braucht Zeit.

"When writing to one of the registers TCNT2, OCR2x, or TCCR2x, the value 
is transferred to a temporary register, and latched after two positive 
edges on TOSC1."

Ist der CPU-Takt zu hoch ist die CPU schon beim Sleep angkommen bevor 
die zwei externen positiven Flanken durch sind und Voila: Sie spielt 
Dornröschen.

Ich kann das Kapitel 18.9 nur empfehlen auch wenn ich die Datenblätter 
diaktisch anstrengend geschrieben finde.

viele Grüße
Carsten

von Peter D. (peda)


Lesenswert?

Das TIMSK2 gehört aber nicht zu diesen Registern, also muß man nach dem 
Schreiben nicht warten.

Allerdings sollte man noch folgendes beachten:

"After a Power-up Reset or wakeup
from Power-down or Standby mode, the user should be aware of the fact 
that this Oscillator
might take as long as one second to stabilize. The user is advised to 
wait for at least one
second before using Timer/Counter2"


Peter

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> Nö, das macht der Interrupthandler.
Macht er eben nicht. Das ist ja nicht das Flag, welches den Interrupt 
ausgelöst hat, sondern das andere.

> Ansonsten hat man doch wieder das gleiche Problem bei zu schnellem
> CPU-Takt.
Das läuft so, wie ich es gepostet habe mit OCR2A = OCR2B. Aber nur, wenn 
man nach dem Umschalten das IR-Flag löscht.

mfg.

von Carsten R. (kaffeetante)


Lesenswert?

Vom TIMSK2 habe ich auch nicht gesprochen.

Aber ein paar Takte vor dem Sleep waren die anderen Kandidaten dran. Ich 
beziehe mich da auf das originale Assemblerlisting.

Kurz vor dem TIMSK2 wird auf alle möglichen Register vom Timer2 
geschrieben. Bald darauf folgt das Sleep-Kommando. Und wir müssen nicht 
2 CPU-Takte warten sondern 2 externe Takte, was bei einem hohen CPU-Takt 
und niedrigem externen Takt eine Menge Befehle sein können die wir im 
Blick haben müssen wenn man die Flags nicht prüft.

Senken wir den CPU-Takt braucht man nicht so weit zurückblicken. Daher 
die scheinbare Taktabhängigkeit. Dabei ist weniger der absolute Takt 
entscheidend sondern das Taktverhältnis CPU zu extern. Und 8 MHz zu 32 
kHz, da sind wir bei ganz grob 500 Takte/Befehle bis wir unsere beiden 
externen positiven Flanken haben. Genau sagen können wir es nicht da wir 
nicht genau wissen wann die Flanke kommt.

Da passt der ganze CODE rein! Somit befindet sich jeder Schreibzugriff 
auf Timer2 im kritischen Zeitfenster.

Bevor wir also weiter herumraten, können wir doch mal mit einer 
Warteschleife auf die Busy-Flags warten und schauen ob es dann geht.

Alternativ kann man auch zwischen Schreibugriff auf Timer2 und LOOP ein 
IDLE-SLEEP machen und dann auf Power-Save umkonfigurieren. Das wäre dann 
Quick and Dirty mit einem einfachen Sleep vor dem setzen des Power-save 
erledigt und sähe dann so aus wie unten. Nicht elegant aber schnell 
getestet.

Dann wissen wir mehr. Wobei ich das LOOP-Label eher vor dem zweiten 
Sleep setzen würde. Man muß ja nicht nach jedem Sleep den Sleep Mode 
erneut auf den selben Wert setzen.

Ich hoffe es ist selbsterklärend welcher Part im Original hierdurch 
ersetzt werden soll.

    sei
    ldi   temp, (1<<SE)               ; IDLE Mode aktivieren
    out   SMCR, temp                  ; in Sleep Mode Control Register
    sleep
    ldi   temp, (1<<SE|1<<SM1|1<<SM0) ; Power Save Mode aktivieren
    out   SMCR, temp                  ; in Sleep Mode Control Register
loop:
    sleep              ; Sleep-Modus einschalten


viele Grüße
Carsten

von Thomas E. (thomase)


Lesenswert?

Carsten R. schrieb:
> Bevor wir also weiter herumraten, können wir doch mal mit einer
> Warteschleife auf die Busy-Flags warten und schauen ob es dann geht.
Klar kann man das machen. Funktioniert ja auch.
Aber es gibt eben auch noch die Möglichkeit der OCR-Umschaltung. Und die 
funktioniert auch. Aber nicht so, wie Peter das beschreibt.

Das:
1
  TCCR2A |= (1 << WGM21);
2
  TCCR2B |= (1 << CS22) | (1 << CS20);
3
  TIMSK2 |= (1 << OCIE2A);
4
  OCR2A = 63;
5
  OCR2B = 31;
6
  ASSR |= (1 << AS2);
7
  //---
8
9
  DDRD |= (1 << 2) | (1 << 3);
10
  PORTD |= (1 << 2);
11
  
12
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
13
  sleep_enable();    
14
15
  sei();
16
17
  while(1)
18
  {
19
    sleep_cpu();            
20
  }
21
22
ISR(TIMER2_COMPA_vect)
23
{
24
  TIMSK2 ^= ((1 << OCIE2A) | (1 << OCIE2B));  
25
  PORTD ^= ((1 << 2) | (1 << 3));
26
}
27
ISR(TIMER2_COMPB_vect, ISR_ALIASOF(TIMER2_COMPA_vect));
funktioniert nicht.

Aber das:
1
//...
2
OCR2A = 63; 
3
OCR2B = 31;
4
//...
5
ISR(TIMER2_COMPA_vect)
6
{
7
  TIMSK2 ^= ((1 << OCIE2A) | (1 << OCIE2B)); 
8
  TIFR2 |= TIMSK2; 
9
  PORTD ^= ((1 << 2) | (1 << 3));
10
}
11
ISR(TIMER2_COMPB_vect, ISR_ALIASOF(TIMER2_COMPA_vect));

Und das:
1
//...
2
OCR2A = 127; 
3
OCR2B = OCR2A;
4
//...
5
6
ISR(TIMER2_COMPA_vect)
7
{
8
  TIMSK2 ^= ((1 << OCIE2A) | (1 << OCIE2B));  
9
  TIFR2 |= TIMSK2;
10
  PORTD ^= ((1 << 2) | (1 << 3));
11
}
12
ISR(TIMER2_COMPB_vect, ISR_ALIASOF(TIMER2_COMPA_vect));

Atmega644a, 8MHz intern
Mit 20MHz extern ist ihm das ganze Gedöns allerdings völlig egal.
Dann läuft auch das:
1
/...
2
ISR(TIMER2_COMPA_vect)
3
{
4
  PORTD ^= ((1 << 2) | (1 << 3));
5
}

genau wie mit 1MHz intern.

mfg.

von Peter D. (peda)


Lesenswert?

Thomas Eckmann schrieb:
> Das läuft so, wie ich es gepostet habe mit OCR2A = OCR2B. Aber nur, wenn
> man nach dem Umschalten das IR-Flag löscht.

So, ich hab jetzt gründlich rumprobiert. Meine Idee funktioniert 
generell nicht, ich hatte sie auch noch nie ausprobiert.
Das war ein Vorschlag bei AVRFreaks gewesen. Klang erstmal gut, aber 
funktioniert nicht.

Warum Dein Code bei Dir funktioniert, liegt einfach daran, daß durch das 
Flag löschen soviel zusätzlicher Code erzeugt wird, daß die nötige 
Wartezeit um ist.
Bei mir sind die internen 8MHz wohl nen Tick zu schnell, daher 
funktioniert Dein Code nicht bei mir. Erst mit noch ein paar NOPs lief 
er.

Fazit: Es gibt keine andere Lösung, als zu warten.

Wenn es eine andere Lösung gäbe, hätte sie Atmel bestimmt auch 
beschrieben. Die sollten ja ihre Chips kennen.


Peter

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> Warum Dein Code bei Dir funktioniert, liegt einfach daran, daß durch das
> Flag löschen soviel zusätzlicher Code erzeugt wird, daß die nötige
> Wartezeit um ist.
Den Verdacht hatte ich auch schon.
Aber das
1
  TIFR2 |= TIMSK2;
2
 208:  97 b3         in  r25, 0x17  ; 23
3
 20a:  80 81         ld  r24, Z
4
 20c:  89 2b         or  r24, r25
5
 20e:  87 bb         out  0x17, r24  ; 23

ist doch nichts, was roundabout 30µs dauert.

Zumal sowas dann auch funktionieren müsste:
1
ISR(TIMER2_COMPA_vect)
2
{
3
  TIMSK2 ^= ((1 << OCIE2A) | (1 << OCIE2B));
4
//  TIFR2 |= TIMSK2;
5
         _delay_us(32);
6
7
  PORTD ^= ((1 << 2) | (1 << 3));
8
}
9
ISR(TIMER2_COMPB_vect, ISR_ALIASOF(TIMER2_COMPA_vect));

Das funktioniert aber nicht. Im Gegensatz zu einem gleichlangen Delay 
ohne OCR-Umschaltung.

> Bei mir sind die internen 8MHz wohl nen Tick zu schnell, daher
> funktioniert Dein Code nicht bei mir. Erst mit noch ein paar NOPs lief
> er.
Interessant. Ich hab' das auf 3 verschiedenen Controllern, 644a, 644p, 
168a, getestet. Es lief immer. Aber die sind sich auch sehr ähnlich. 
Zumal alle bei gleicher Temperatur liefen.

Ist alles ein bisschen sehr merkwürdig. Und vor allen Dingen unlogisch. 
Zumindest mit User-Kenntnissen.

Peter Dannegger schrieb:
> Fazit: Es gibt keine andere Lösung, als zu warten.
Ja. Alles andere riecht nach Zufall.

Auf welchem Controller hast du das probiert?

mfg.

von Peter D. (peda)


Lesenswert?

Thomas Eckmann schrieb:
> ist doch nichts, was roundabout 30µs dauert.

Nicht alleine. Aber es bringt Z mit ins Spiel, d.h. R30,31 müssen 
gesichert werden. Insgesamt habe ich 56 Zyklen gezählt.
Du kannst ja mal den Interrupt nackt definieren und am Schluß ein RETI 
machen. Dann fällt schonmal das ganz Push/pop Gedöns weg und wird 
ordentlich schneller.

Ich habe im Interrupt den ganzen Port gezählt, damit man die 
Mehrfachausführung gut sieht.
LED0,1 flackerten nur kurz, erst ab LED2 zählte hoch. Er hat also immer 
4 Interrupts auf einen Rutsch gemacht.

Wenn man die Zeile "sleep_enable();" auskommentiert, läuft es immer 
korrekt.
D.h. erst das Power-Save läßt die Bits verrückt spielen.

Thomas Eckmann schrieb:
> Ja. Alles andere riecht nach Zufall.

Stimmt.
Ein NOP reichte, um statt 4 Interrupts normal zu zählen, klingt wirklich 
sehr zufällig.

Schade, daß Atmel nicht näher auf die Innenschaltug der Synchronisation 
eingeht.
Beim ATtiny261 ist ja ein ähnliche Problem (PLL), da haben sie ein 
schönes Bild dazu:
Figure 12-2. Timer/Counter1 Synchronization Register Block Diagram

Thomas Eckmann schrieb:
> Auf welchem Controller hast du das probiert?

ATtiny48 auf dem STK500. Ich mußte daher das EXCLK-Bit setzen.


Peter

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> D.h. erst das Power-Save läßt die Bits verrückt spielen.
Interessanterweise funktioniert es mit 20MHz. Dabei ist die ganze 
"Wachzeit" ca. 400µs. Kann man gut sehen, wenn man den CLKOUT-Pin 
freischaltet. Bei 8MHz intern kann ich das auf meinem Analog-Oszi kaum 
noch erkennen.

> Ein NOP reichte, um statt 4 Interrupts normal zu zählen, klingt wirklich
> sehr zufällig
Ich hab mal per OSCCAL den internen Takt hochgeschraubt. Irgendwann geht 
es dann nicht mehr. Kleines Delay rein und schon läuft es wieder. Also 
es ist eindeutig die Zeit.

mfg.

von NobbyH (Gast)


Lesenswert?

Carsten R. schrieb:
> "When writing to one of the registers TCNT2, OCR2x, or TCCR2x, the value
>
> is transferred to a temporary register, and latched after two positive
>
> edges on TOSC1."

Hallo,
genau das ist meiner Meinung nach der richtige Lösungsansatz:
Habe das Programm von Sascha wie folgt verändert:
          .
          .
  ;Timer 2  ;Uhrzeit
    ldi  tempu1,(1<<AS2)
    sts  ASSR,tempu1

    ldi  tempu1,(1<<TOIE2)
    sts  TIMSK2,tempu1

    ldi  tempu1,(1<<CS20)|(1<<CS21)|(1<<CS22)
    sts  TCCR2B,tempu1

next:
    ldi  tempu1, ASSR
    sbrc temp, 0
    rjmp next

    sei
        .
        .

Dies löst mein Problem bei den verschiedenen Clock Teilerfaktoren.
Beim internen 8MHz Takt ist mir lediglich aufgefallen, dass er immer 4 
Takte zählt. Dafür suche ich noch eine Lösung.

Gruß
NobbyH

von Peter D. (peda)


Lesenswert?

Man muß es genau so machen, wie es im Datenblatt steht. Dann klappt es 
auch mit dem Power-Save und bei jeder Frequenz.
Also Dummy-Write im Interrupt und mit dem Power-Save warten, bis Busy 
gelöscht ist.
1
//                             Target: ATmega48
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
#include <avr/power.h>
5
6
void init()
7
{
8
  PORTD = 0x7F;
9
  DDRD = 0xFF;
10
  clock_prescale_set( clock_div_1 );    // 8MHz
11
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
12
  sleep_enable();    
13
14
  ASSR = 1<<EXCLK;                      // use 32kHz from STK500
15
  ASSR = 1<<AS2 | 1<<EXCLK;             // When the value of AS2 is changed,
16
                                        // the contents of TCNT2, OCR2A, OCR2B,
17
                                        // TCCR2A and TCCR2B might be corrupted
18
  TCCR2A = 1<<WGM21;                    // Mode 2: CTC
19
  TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; // F_T2 / 1024
20
  OCR2A = 31;                           // 1s
21
  TIMSK2 = 1<<OCIE2A;
22
}
23
24
int main()
25
{
26
  init();
27
  for(;;){
28
    cli();
29
    if( !(ASSR & (1<<TCN2UB | 1<<OCR2AUB | 1<<OCR2BUB | 1<<TCR2AUB | 1<<TCR2BUB))){
30
      sei();                            // no interrupt prior sleep !
31
      sleep_cpu();                      // only, if all T2 changes done ! 
32
    }
33
  }
34
}
35
36
ISR(TIMER2_COMPA_vect )
37
{
38
  OCR2A = 31;                           // dummy write
39
  PORTD--;
40
}

Peter

von Thomas E. (thomase)


Lesenswert?

NobbyH schrieb:
>Dies löst mein Problem bei den verschiedenen Clock Teilerfaktoren.
Die Teilerfaktoren sind egal. Das Problem ist , daß die Werte in die 
Timer2-Register nicht mit dem schnellen CPU-Takt, sondern mit dem 
langsamen Uhrentakt gelatcht werden. Das passiert aber zumindest immer 
mit 32KHz, unabhängig vom Prescaler für den TCNT.
> Beim internen 8MHz Takt ist mir lediglich aufgefallen, dass er immer 4
> Takte zählt. Dafür suche ich noch eine Lösung.
Das ist genau das Problem, über das wir hier seit ein paar Tagen 
fachsimpeln.
Wobei die ersten Lösungsansätze nicht zum Erfolg führten bzw. das 
Problem zufällig verdrängt haben (OCR-Umschaltung). Dadurch wurde die 
ISR nämlich gerade lang genug. Allerdings nicht, wenn der 
8MHz-Oszillator ein wenig mehr nach oben gestreut hat.

Es gibt 2 Lösungsansätze:
1. Die ISR ist so lang, daß die Zeitbedingung erfüllt wird, notfalls mit 
einem Delay oder ein paar NOPs. Das macht die ISR aber entweder unnötig 
lang oder funktioniert unzuverlässig, da durch die Toleranzen des 
internen Oszillators es auch mal ein NOP zu wenig sein kann.

2. Die beste, weil immer funktionierende, Lösung: Man macht einen 
Dummy-Write auf eines der Timer2-Register und fragt das zugehörige 
Busy-Flag ab.
Damit erreicht man die optimale Ausführungszeit. So kurz wie möglich, 
aber immer so lang wie nötig. So wie Peter das gepostet hat. Wobei es 
für die Funktion egal ist, ob man die Flag-Abfrage vor dem sleep_cpu in 
der main oder noch vor dem Verlassen der ISR macht. Vom Timing her ist 
es allerdings günstiger das in der main zu machen, da dann die Wartezeit 
auf das Busy-Flag zumindest teilweise durch die POPs und das RETI 
sinnvoll genutzt wird.
Den Dummy-Write dagegen macht man grundsätzlich in der ISR als erstes.

mfg.

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.