Forum: Compiler & IDEs AVR: µC geht nicht in den Idle-Mode


von ghost (Gast)


Angehängte Dateien:

Lesenswert?

Hy Leute,

Ich arbeite gerade mit dem ATMEGA32L und soll so energiesparend wie 
möglich programmieren. Dafür möchte ich alle _delay_ms(); durch eine 
eigene Routine ersetzen, welche den µC für diese Zeit in den Idle-Mode 
schickt.
Ich habe das Programm außerhalb meines Hauptprogramms zur Testung 
geschrieben.
Da die Variante mit Timer1 und ISR(TIMER1_COMPA_vect); nicht 
funktioniert hat, kommentierte ich diese aus und versuchte es mit einem 
externen Interrupt.
Doch leider komm ich auf das selbe Ergebnis, die LED (welche mir als 
Kontrolle dient) schaltet sich ca. alle 5s ein und nach weiteren 5s 
wieder aus.

Ich konnte in anderen Beiträgen, bzw. im GCC Tutorial keinen Unterschied 
zu meinem Programm entdecken...

Vielen Dank im Voraus!

von Thomas E. (thomase)


Lesenswert?

ghost schrieb:
> Da die Variante mit Timer1 und ISR(TIMER1_COMPA_vect); nicht
> funktioniert hat,

Das geht auch nicht. Im Sleep ist der Takt abgeschaltet. Dann hat der 
Timer nichts zum zählen.

ghost schrieb:
> Doch leider komm ich auf das selbe Ergebnis, die LED (welche mir als
> Kontrolle dient) schaltet sich ca. alle 5s ein und nach weiteren 5s
> wieder aus.

Wo kommen denn die 5s her? Und wieso dasselbe Ergebnis? Dasselbe wie 
was?

mfg.

von Rolf M. (rmagnus)


Lesenswert?

Thomas Eckmann schrieb:
> ghost schrieb:
>> Da die Variante mit Timer1 und ISR(TIMER1_COMPA_vect); nicht
>> funktioniert hat,
>
> Das geht auch nicht. Im Sleep ist der Takt abgeschaltet.

Nein.

> ghost schrieb:
>> Doch leider komm ich auf das selbe Ergebnis, die LED (welche mir als
>> Kontrolle dient) schaltet sich ca. alle 5s ein und nach weiteren 5s
>> wieder aus.
>
> Wo kommen denn die 5s her?

Ich hätte 2,2 erwartet, wenn das Schlafen nicht funktioniert.

> Und wieso dasselbe Ergebnis? Dasselbe wie was?

Das selbe wie mit Timer?

ghost schrieb:
> #define EXT_INT0_PIN    PD3
> #define EXT_INT1_PIN    PD2

Das ist falsch. Laut Datenblatt ist PD2 INT0 und PD3 INT1.

von Peter D. (peda)


Lesenswert?

Thomas Eckmann schrieb:
> Das geht auch nicht. Im Sleep ist der Takt abgeschaltet. Dann hat der
> Timer nichts zum zählen.

Wasn Quatsch.
Natürlich läuft alle Peripherie im Idle weiter, nur die CPU wird 
angehalten.
Die Stromeinsparung ist daher auch nicht sonderlich hoch (~50%).

von Thomas E. (thomase)


Lesenswert?

Peter Dannegger schrieb:
> Wasn Quatsch.

Autsch, Idle. Ja, ich steh schon in der Ecke.

mfg.

von Thomas P. (ghost_2303)


Lesenswert?

Ich weiß leider auch nicht warum es 5s sind... die Frequenz (soft- und 
hardwaretechnisch) stimmt.

Das mit INT0 und INT1 ist in diesem Fall egal, weil beide gleich 
beschaltet werden, aber danke.

Ja genau, ich erhalte mit externem Interrupt und Timer1 dasselbe 
Ergebnis => kein Idle-Mode, LED schaltet sich alle 5s ein und aus. Auch 
der Stromverbrauch sinkt nicht während der Messung nicht(nur der Strom 
der LED bei EIN/AUS).

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Thomas P. schrieb:
> LED schaltet sich alle 5s ein und aus.

Dann stimmt Dein F_CPU nicht.
Im Code hast Du ja 10 * 0,2s stehen.

Du mußt das nicht verklausulieren, _delay_ms verträgt bis 6,5535s.

Was sollen die Delays überhaupt, schmeiß sie raus und nimm den Timer.

von Thomas P. (ghost_2303)


Lesenswert?

Mein F_CPU ist auf 1MHz eingestellt, softwaretechnisch und 
hardwaretechnisch.

Auszug aus delay.h:
"The maximal possible delay is 262.14 ms / F_CPU in MHz."

Die Delays habe ich, um den Strom besser messen zu können.
Ich glaube nicht, dass es an den _delay_ms() liegt...

von Ahab (Gast)


Lesenswert?

Thomas P. schrieb:
> Auszug aus delay.h:
> "The maximal possible delay is 262.14 ms / F_CPU in MHz."

Wenn's danach nicht weitergeht hast du aber noch eine sehr, sehr alte 
Version davon installiert.

Die nächsten Sätze wären nämlich:


When the user request delay which exceed the maximum possible one, 
_delay_ms() provides a decreased resolution functionality. In this mode 
_delay_ms() will work with a resolution of 1/10 ms, providing delays up 
to 6.5535 seconds (independent from CPU frequency). The user will not be 
informed about decreased resolution.


Und die "decreased resolution" ist sicher noch besser als 
Selbst-Zusammenstöpseln von Einzel-Delays in einer Schleife.

von Thomas P. (ghost_2303)


Lesenswert?

Ich weiß, dass es nachher noch weiter geht! Aber ich wollte die maximal 
erreichbare Genauigkeit, da ich mit dem Oszi alles kontrolliere.

Aber darum geht es ja jetzt nicht. Ich möchte wissen, ob jemand einen 
Tipp hat, warum mein µC nicht in den Idle-Mode geht bzw. was ich 
vergessen oder falsch gemacht habe!

von Thomas E. (thomase)


Angehängte Dateien:

Lesenswert?

Thomas P. schrieb:
> Aber darum geht es ja jetzt nicht. Ich möchte wissen, ob jemand einen
> Tipp hat, warum mein µC nicht in den Idle-Mode geht bzw. was ich
> vergessen oder falsch gemacht habe!

Ich habe mir Mal erlaubt, dein Programm in der Timerversion laufen zu 
lassen.

Atmega644, 1MHz, Prescaler = 64
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <stdlib.h>
5
#include <avr/sleep.h>
6
7
#define EXT_INT0_REG    DDRD
8
#define EXT_INT1_REG    DDRD
9
#define EXT_INT0_PORT    PORTD
10
#define EXT_INT1_PORT    PORTD
11
#define EXT_INT0_PIN    PD3
12
#define EXT_INT1_PIN    PD2
13
14
15
int main(void)
16
{      
17
  DDRA = 0x00;
18
  DDRB = 0x00;
19
  DDRC = 0x00;
20
//  DDRD = 0x00;
21
  PORTA = 0x00;
22
  PORTB = 0x00;
23
  PORTC = 0x00;
24
//  PORTD = 0x00;
25
  
26
  ACSR |= (1<<ACD);
27
  
28
  EXT_INT0_REG &= ~(1<<EXT_INT0_PIN);    // activate external interruptports
29
  EXT_INT0_PORT |= (1<<EXT_INT0_PIN);
30
  
31
//  GICR |= (1<<INT0);
32
  
33
  TCCR1B |= (1<<CS11) | (1<<CS10) | (1<<WGM12);  // Prescaler 64 
34
  TIMSK1 |= (1<<OCIE1A);
35
  OCR1A = 5*975;    // max.: 975 => jede Sekunde ein Interrupt
36
            //
37
            // => 975/10 = 97 => alle 100ms ein Interrupt
38
39
  while(1)
40
  {
41
    DDRD &= ~(1<<PD7);
42
    PORTD &= ~(1<<PD7);
43
    
44
    for(uint8_t cnt_1 = 0; cnt_1 <=10; cnt_1++)
45
    {
46
      _delay_ms(200);
47
    }
48
  
49
    set_sleep_mode(SLEEP_MODE_IDLE);
50
    sei();
51
    sleep_enable();
52
    TCNT1 = 0;
53
    sleep_cpu();
54
    sleep_mode();
55
56
    sleep_disable();    
57
    DDRD |= (1<<PD7);
58
    PORTD |= (1<<PD7);
59
    
60
    for(uint8_t cnt_1 = 0; cnt_1 <=10; cnt_1++)
61
    {
62
      _delay_ms(200);
63
    }
64
  }
65
}
66
67
ISR(TIMER1_COMPA_vect)
68
{
69
  ;
70
}

Der Sleepmode wird erreicht und es läuft mit dem erwarteten Timing.

TIMSK1 muss in TIMSK geändert werden. Dann muss das bei dir auch laufen.

mfg.

: Bearbeitet durch User
von Thomas P. (ghost_2303)


Lesenswert?

Beim ATMEGA32 gibt es leider kein TIMSK1...

von Thomas E. (thomase)


Lesenswert?

Thomas P. schrieb:
> Beim ATMEGA32 gibt es leider kein TIMSK1...

Thomas Eckmann schrieb:
> TIMSK1 muss in TIMSK geändert werden.

mfg.

von Thomas P. (ghost_2303)


Lesenswert?

Ah, ok, das hab ich falsch verstanden!

Ich dachte, sleep_mode() ist die Zusammenfassung von sleep_enable() und 
sleep_cpu()? Warum benötige ich hier beides?

Ich kann leider trotzdem keine Änderung des Stromes messen... 17mA (ohne 
Status-LED), weniger bekomm ich nicht!

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Peter Dannegger schrieb:
> Dann stimmt Dein F_CPU nicht.
> Im Code hast Du ja 10 * 0,2s stehen.

11 * 0,2 s.

Thomas P. schrieb:
> Die Delays habe ich, um den Strom besser messen zu können.

Ja, aber wenn da 5 statt 2,2 Sekunden rauskommen und die Taktrate 
korrekt für das Delay angegeben ist, sind das 2,8 Sekunden zuviel. Die 
müssen ja irgendwo herkommen.

Thomas P. schrieb:
> Ich weiß, dass es nachher noch weiter geht! Aber ich wollte die maximal
> erreichbare Genauigkeit, da ich mit dem Oszi alles kontrolliere.

Die verlierst du durch deine Schleife außenrum aber auch. Und wenn der 
Takt vom internen R/C-Oszillator stammen sollte, ist der außerdem noch 
deutlich ungenauer.

von Thomas E. (thomase)


Lesenswert?

Thomas P. schrieb:
> Ich dachte, sleep_mode() ist die Zusammenfassung von sleep_enable() und
> sleep_cpu()? Warum benötige ich hier beides?

Benötigst du nicht. Da habe ich einfach nur zuviel wieder 
"einkommentiert".

Ich hab das Ganze jetzt ein wenig zusammengestrichen:

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <stdlib.h>
5
#include <avr/sleep.h>
6
7
int main(void)
8
{        
9
  ACSR |= (1<<ACD);
10
  
11
  TCCR1B |= (1<<CS11) | (1<<CS10) | (1<<WGM12);  // Prescaler 64 
12
  TIMSK1 |= (1<<OCIE1A);
13
  OCR1A = 5*975;    // max.: 975 => jede Sekunde ein Interrupt
14
            //
15
            // => 975/10 = 97 => alle 100ms ein Interrupt
16
  
17
  DDRD |= (1<<PD7);
18
  set_sleep_mode(SLEEP_MODE_IDLE);
19
  sleep_enable();
20
  sei();
21
22
  while(1)
23
  {
24
    PORTD &= ~(1<<PD7);
25
    
26
    //for(uint8_t cnt_1 = 0; cnt_1 <=10; cnt_1++) _delay_ms(200);
27
    _delay_ms(2200);
28
29
    TCNT1 = 0;
30
    sleep_cpu();
31
 
32
    PORTD |= (1<<PD7);
33
    
34
//   for(uint8_t cnt_1 = 0; cnt_1 <=10; cnt_1++) _delay_ms(200);
35
   _delay_ms(2200);
36
  }
37
}
38
39
ISR(TIMER1_COMPA_vect)
40
{
41
}

Thomas P. schrieb:
> Ich kann leider trotzdem keine Änderung des Stromes messen... 17mA (ohne
> Status-LED), weniger bekomm ich nicht!

Sleep Idle ist nicht sehr effektiv.

Bei mir beträgt der Unterschied 1,3mA (84,4mA : 83,1mA).
Auf meinem Testboard ist aber noch einiges mehr drauf. Der 644P 
verbraucht ca. 10mA bei 5V. Also gut 10% Ersparnis im Sleep.

Bei 3V beträgt der Unterschied nur noch 0,4mA.

mfg.

von Thomas P. (ghost_2303)


Lesenswert?

Danke Thomas. Ich glaub das war es jetzt.

Ich komme jetzt auch auf diese Werte!

Vielen Dank für die Hilfe!

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.