Hallo Forum,
ich bin am Verzweifeln mit meinem ATMega328P. Eigentlich möchte ich ihn
als batteriebetriebenen Sensor arbeiten lassen, schlafen lassen um
Batterie zu sparen und ab und zu kurz Aufwecken.
Um das Ganze vorzubereiten habe ich einen 328P auf einem Breadboard mit
16MHz Quartz und 2 Kondensatoren, versorgt über ein 5V Netzteil
(Minimalkonfiguration).
Ich habe mich mit ordentlich mit den verschiedenen PowerSave Features
des Mega beschäftigt und wollte jetzt ein kleines Programm schreiben, um
den uC schlafen zu legen.
Mein Problem ist, es funktioniert einfach nicht. Die Stromaufnahme
sollte nach ausführen des Codes auf einige uA zurückgehen, tatsächlich
springt der Stromverbrauch aber nur von 40mA (Test LED an) zu 20mA (Test
LED aus, CPU off).
Da die Konfiguration auch Arduino kompatibel ist und es da zwei
interessante Powersave Libraries (jeelib, rocketscream lowPower) gib,
habe ich es mal damit versucht -> gleiches Verhalten! Zudem habe ich mal
auf den internen Oszillator umgeschaltet -> ändert auch nichts.
Es muss eigentlich am Mega liegen, aber wie erklärt man sich das? Ich
habe die Dinger in China bestellt ... eventuell doch kein echter ATMega?
Kann mir bitte jemand helfen und sagen ob ich irgendwas übersehe?
Code:
normaler avr code
1
#include<avr/io.h>
2
#include<avr/power.h>
3
#include<avr/sleep.h>
4
#include<avr/interrupt.h>
5
6
#include<stdio.h>
7
#include<stdlib.h>
8
9
intmain()
10
{
11
PRR=0xff;
12
13
// Set up a pin for an LED.
14
DDRB=0x01;
15
16
// Enable the interrupt as a falling edge.
17
EICRA=(1<<ISC01);
18
EIMSK=(1<<INT0);
19
20
while(1)
21
{
22
// Go to sleep until interrupted.
23
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
24
sleep_enable();
25
26
// Must enable or can't wake!
27
sei();
28
sleep_cpu();
29
30
// Zzz...
31
32
sleep_disable();
33
34
// Do stuff like toggle a bit.
35
PINB|=0x01;
36
}
37
}
38
39
ISR(INT0_vect)
40
{
41
// Empty. We don't care - we just want it to wake the CPU!
42
}
arduino code
1
#include"test.h"
2
#include"LowPower.h" //rocketscream library
3
4
5
voidsetup(){
6
// initialize digital pin LED_BUILTIN as an output.
7
pinMode(3,OUTPUT);
8
}
9
10
// the loop function runs over and over again forever
11
voidloop(){
12
digitalWrite(3,HIGH);// turn the LED on (HIGH is the voltage level)
13
delay(5000);// wait for a second
14
digitalWrite(3,LOW);// turn the LED off by making the voltage LOW
Und das Datenblatt lesen - hier mal aus dem Mega328 DB:
" Unconnected pins
If some pins are unused, it is recommended to ensure that these pins
have a defined level. Even though most of the digital inputs are
disabled in the deep sleep modes as described above, floating inputs
should be avoided to reduce current consumption in all other modes where
the digital inputs are enabled (Reset, Active mode and Idle mode). The
simplest method to ensure a defined level of an unused pin, is to enable
the internal pull-up. In this case, the pull-up will be disabled during
reset. If low power consumption during reset is important, it is
recommended to use an external pull-up or pull-down. Connecting unused
pins directly to VCC or GND is not recommended, since this may cause
excessive currents if the pin is accidentally configured as an output."
Also alle Pins mit Pegel vorbelegen und nicht nur die benötigten. Edit:
Herr Landolt war schneller.
Das hier:
tobiasm schrieb:> // Do stuff like toggle a bit.> PINB |= 0x01;
Toggle'd nicht nur ein Bit, sondern je nach aktuellem Spannungslevel am
PortB auch mehrere. (und schaltet damit ggfs. die Pull-Ups aus)
Zum Togglen wäre
PINB = 0x01;
ausreichend.
und zum Aufwachen aus dem PWR_DOWN brauchst du einen LEVEL-Interrupt,
keinen Edge-Triggered.
Aber das würde nur zuwenig Stromverbrauch erklären, nicht zuviel.
Εrnst B. schrieb:> Toggle'd nicht nur ein Bit, sondern je nach aktuellem Spannungslevel am> PortB auch mehrere. (und schaltet damit ggfs. die Pull-Ups aus)
Doch. Nur eins.
1
PINB|=(1<<0);
2
49c:b09asbi0x16,0;22
Während der am Ziel vorbeigeht:
Εrnst B. schrieb:> Zum Togglen wäre>> PINB = 0x01;>> ausreichend.
1
PINB=(1<<0);
2
49c:81e0ldir24,0x01;1
3
49e:86bbout0x16,r24;22
Εrnst B. schrieb:> und zum Aufwachen aus dem PWR_DOWN brauchst du einen LEVEL-Interrupt,> keinen Edge-Triggered.
Mit Pinchange geht es auch. Aber mit INTx nur mit Level.
Thomas E. schrieb:> Doch. Nur eins.
Haarspalterei:
Nur wegen eingeschalteter Optimierung, die hier etwas über's Ziel
hinausschießt.
Bei vollem Read-Modify-Write würden (zusätzlich zu Bit 0) alle
PORTB-Outputs getoggle'd, die gerade im PINB als HIGH gelesen werden.
Code, der abhängig von der Compiler-Optimierungsstufe mal geht, und mal
komplett falsch ist, ist mir suspekt.
Thomas E. schrieb:> PINB = (1 << 0);> 49c: 81 e0 ldi r24, 0x01 ; 1> 49e: 86 bb out 0x16, r24 ; 22
Ist exakt richtig. BIT0 im PORTB wird getoggled. Unabhängig von
Compilersettings.
Thomas E. schrieb:> Mit Pinchange geht es auch. Aber mit INTx nur mit Level.
-> Siehe:
tobiasm schrieb:> EICRA = (1<<ISC01);
Er versucht es mit Falling Edge am INT0.
Εrnst B. schrieb:> Nur wegen eingeschalteter Optimierung, die hier etwas über's Ziel> hinausschießt.
Die schießt nicht übers Ziel hinaus, sondern ist die einzige
Möglichkeit, dieses Feature in C einzusetzen. Einmal abgesehen von
Inline-Asm.
Thomas E. schrieb:> Die schießt nicht übers Ziel hinaus, sondern ist die einzige> Möglichkeit, dieses Feature in C einzusetzen.
Quatsch. Das "PINB = (1 << 0);" funktioniert auch. Es muss nur eine 1 in
das jeweilige Bit geschrieben werden. Das muss nicht zwangsläufig mit
sbi geschehen, ein out tut's auch.
Stefan E. schrieb:> Quatsch.
Quatsch ist das, was du schreibst.
Es geht nicht darum, was auch funktioniert, sondern was am effektivsten
ist. Und das ist das Toggeln eines Portbits mit einem sbi aufs PIN-Reg..
Den erhält man aber nur mit OR bei eingeschalteter Optimierung. Da dann
nämlich kein RMW ausgeführt wird.
Thomas E. schrieb:> Es geht nicht darum, was auch funktioniert, sondern was am effektivsten> ist.
Nö, denn du schriebst "einzige Möglichkeit", und nicht etwa "effektivste
Möglichkeit"
Vielen Dank für eure rege Beteiligung, finde ich super.
Habe jetzt alle nicht belegten Pins auf Eingang gestellt und die
internen Pullups aktiviert -> ändert nicht viel, jetzt habe ich 18,8mA
im "Tiefschlaf".
Irgendwas stimmt da doch gewaltig nicht...
> Irgendwas stimmt da doch gewaltig nicht...
In der Tat.
Mir ist unklar, was auch schon Ernst B. anmerkte, wie überhaupt der
Interrupt ausgelöst und damit in der Folge die LED umgeschaltet werden
kann, denn auf INT0 kommt man nur mit einem Level-Interrupt aus dem
Power-Down-Mode heraus, eingestellt wurde aber fallende Flanke.
Rund 20 mA lassen sich tatsächlich nicht mit freischwebenden Eingängen
erklären, also wie steht es mit dem Hinweis von Thomas Eckmann bezüglich
des abgehängten Programmiergeräts?
Pardon - das mit der Flanke auf INT0 und Power-Down-Mode scheint doch zu
funktionieren, ich habe es eben ausprobiert; sehe ich so zum ersten Mal.
Wie auch immer - ich komme hier auf unter 0.2 uA.
Und nochmals pardon - freischwebende Eingänge gibt es im Power-Down-Mode
gar nicht, das hatte mir hier mal jemand erklärt, aber mein
Gedächtnis...
siehe Datenblatt '18.2.5. Digital Input Enable and Sleep Modes'
Stefan E. schrieb:> Nö, denn du schriebst "einzige Möglichkeit"
Vielleicht solltest du auch mal über den Zusammenhang, in dem ich das
geschrieben habe, nachdenken und nicht einfach blöd drauflosschreiben.
Thomas E. schrieb:> Die schießt nicht übers Ziel hinaus, sondern ist die einzige> Möglichkeit, dieses Feature in C einzusetzen. Einmal abgesehen von> Inline-Asm.
Extra für dich:
Dabei geht es darum, daß mit dem sbi ein Pin getoggelt wird. Das ist die
einzige Möglichkeit, diesen effektiven Befehl einzusetzen. Außer mit
Inline-Asm. Damit geht es auch.
Der Quarz wird mit den Einstellungen gar nicht benutzt. Ob der dann
allerdings Strom zieht, wage ich zu bezweifeln (die Pins sind ja nicht
high).
Ich kann mir gerade nur vorstellen, dass dein INT0 ständig getriggert
wird oder so.
PS: Achso, aufwachen aus Power-down geht nur mit INT0 level interrupt,
nicht edge. Eventuell ist das ein Nebeneffekt, wenn man das nicht
beachtet. Dann müsstest du natürlich noch den Pullup am entsprechenden
Pin einschalten, sonst ist er ja dauernd low...
> ... L:42 ...
Also: auf dem Steckbrett sitzt der ATmega328P mit 2 GND- und 2
Vcc-Verbindungen, zusätzlich die LED mit Widerstand sowie vermutlich ein
Widerstand an INT0, an welchem auch der Taster angeschlossen ist - sonst
nichts. In der Vcc-Leitung hängt das Amperemeter - zeigt bei mir 0.1 uA
(genauer geht's nicht).
S. Landolt schrieb:> Mit Low-Fuse-Byte=$42 beträgt der Strom bei 5.0 V ohne sleep ca. 1> mA> - mir ist völlig unklar, woher die knapp 20 kommen.
Dann ist meine Verschwörungstheorie doch richtig und der China ATMega
ist halt doch nur ein Nachbau mit einem wesentlich höheren
Stromverbrauch.
Ich kanns nicht ändern, der Chip hängt nackt ohne Resonator auf dem
Breadboard. Jetzt kann höchstens noch mein Messgerät im Eimer sein :/
tobiasm schrieb:> Die Stromaufnahme> sollte nach ausführen des Codes auf einige uA zurückgehen, tatsächlich> springt der Stromverbrauch aber nur von 40mA (Test LED an) zu 20mA (Test> LED aus, CPU off).
Ich hoffe, da hängt nicht noch weitere Peripherie dran.
Mit der lowpower.h (Anhang) bekomme ich auf einem China ProMini um 7µA,
AT328 bei 5V Versorgung und 16MHz. Der wacht alle x-Sekunden auf, zählt
einen hoch und geht wieder schlafen. Natürlich habe ich den
Spannungsregler und die LED auf der Versorgungsleitung abgelötet.
Mit den angehängten .ino macht er alle 8s die LED13 an, dabei fließen um
17mA.
Zu lesen gibt es das dort:
http://www.home-automation-community.com/arduino-low-power-how-to-run-atmega328p-for-a-year-on-coin-cell-battery/
Wie Dir schon geschrieben wurde, geht natürlich noch weniger - mir ist
es erstmal egal, weil meine Versorgung um 10Ah hat und weitere Last dran
hängt.
Naja, ist vielleicht ein ganz einfacher, blöder Fehler und dem OP
peinlich... aber es wäre bestimmt für andere auch hilfreich, zu
erfahren, wie es weitergegangen ist. Letztlich sind es ja ziemlich oft
solche Sachen...
Thomas E. schrieb:> Die schießt nicht übers Ziel hinaus, sondern ist die einzige> Möglichkeit, dieses Feature in C einzusetzen.
Ist jetzt zwar Off-Topic, aber nochmal zur Klarstellung, was ich meine:
1
PINB=0x00;// Toggle nothing
2
PINB=0x01;// Toggle PB0
3
PINB=0x03;// Toggle PB0 and PB1
verhalten sich, unabhängig von Compilerversion und Optimierungsstufe,
immer so wie im Datenblatt angegeben.
Hingegen
1
PINB|=0x00;// Toggle "random" Pins
2
PINB=PINB;// Same.
3
PINB|=0x01;// Toggle only PB0 with -Ox, Toggle PB0+random with -O0
4
PINB|=0x03;// Toggle PB0, PB1 and random other bits.
Verhält sich nur in einer Version so wie sich der Programmierer das
vermutlich gedacht hat, und das auch erst seit GCC Version 2.X?, als die
SBI-Optimierungen dazugekommen sind.
Welches nun das "Richtige" oder "Falsche" Verhalten ist, mag jemand mit
tiefem Verständnis des C-Standards entscheiden, mir jedenfalls ist Code,
der abhängig vom Optimierungslevel unterschiedliche Bedeutungen hat,
suspekt.
Karl M. schrieb:> das beste ist, wenn zwei Ausgänge, USB-TX <--> AVR RX gegeneinander auf> einem Arduinoboard arbeiten !
Wo siehst du 2 Pins gegeneinander arbeiten?
Ich glaube, das hast du dir nur mal ebenso zwischendurch ausgedacht....