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
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
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
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
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
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
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.
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
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
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
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.
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.
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
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
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
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
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.
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
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:
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
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:97b3inr25,0x17;23
3
20a:8081ldr24,Z
4
20c:892borr24,r25
5
20e:87bbout0x17,r24;23
ist doch nichts, was roundabout 30µs dauert.
Zumal sowas dann auch funktionieren müsste:
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.
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
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.
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
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
voidinit()
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,
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.