Forum: Compiler & IDEs AD-Wandlung + Power SAVE


von Alex (Gast)


Lesenswert?

Hallo zusammen

Ich habe hier ein kleinen Programm geschrieben, bei dem eine LED immer 
dann angehen soll, wenn die LDR am AD-Eingang einen hohen Wert liefert.

Zusätzlich versetze ich das Programm auch in den Power Save Modus um 
Strom zu sparen. Der derzeit eingebaute 18Mhz Quarz wird später noch 
durch einen 32kHz Quarz ersetzt um lange Schlafzeiten zu erhalten.

Vor Eintritt in den Power Save schalte ich den AD-Wandler aus und nach 
eine Schlafzeit von 2s wird eine Flag gesetzt, die die INterrupts 
deaktiviert, den AD-Wandler aktiviert und eine Messung durchführt. Je 
nach Ergebnis soll dann die LED eingeschaltet und anschließen die 
Interupts aktiviert werden.

Es hat alles eigentl ganz gut funktioniert, bevor ich das mit dem AD 
ein- und ausschalten programmiert hab. Wisst ihr was ich da vllt falsch 
gemacht hab?

1
int main(void)
2
3
{
4
  //initLCD_Timer2();        // Initialiserung LCD und Timer2 OVF
5
  
6
    ASSR  = (1<< AS2);              // Timer2 asynchron takten
7
    _delay_ms(1000);                 // Einschwingzeit des 18MHz Quarzes
8
    TCCR2B  = 2;                    // Vorteiler 8 -> 0,1111ms Überlaufperiode
9
    while((ASSR & (1<< TCR2BUB)));  // Warte auf das Ende des Zugriffs
10
    TIFR2   = (1<<TOV2);            // Interrupts löschen (*)
11
    TIMSK2 |= (1<<TOIE2);           // Timer overflow Interrupt freischalten
12
13
  initLDR();              // LDR initialiseren
14
  
15
  ACSR = 0x80;            // AD ausschalten
16
  
17
  sei();                // Interrupts aktivieren
18
    
19
  while(1)
20
  
21
  {  
22
        OCR2B = 0;                       // Dummyzugriff
23
        while((ASSR & (1<< OCR2BUB)));   // Warte auf das Ende des Zugriffs
24
 
25
        set_sleep_mode(SLEEP_MODE_PWR_SAVE);
26
        sleep_mode();                   // in den Schlafmodus wechseln
27
28
    if (GO==1)
29
    
30
    {      
31
      cli();
32
      ACSR &= ~(1<<ACD);        // A/D aktivieren
33
      Leuchtstaerke();
34
      GO=0;
35
    
36
37
      if (wert>=240)
38
      {
39
        SET_BIT(DDRB, DDB2);            
40
        CLR_BIT(PORTB, PB2);    // LED einschalten, falls an
41
        _delay_ms(1000);
42
        sei();  
43
      }  
44
45
46
      if (wert<240)
47
48
      {
49
        SET_BIT(DDRB, DDB2);
50
        SET_BIT(PORTB, PB2);    // LED ausschalten, falls an
51
        sei();
52
      }
53
    }
54
  }      
55
}
56
57
58
//=================================================================================================
59
60
// Interrupt Routine===============================================================================
61
62
// Interrupt Timer 2 (8bit) 18432000/256=72000hz  / pre8 = 9000hz  => 0,11111ms/OVF
63
64
65
ISR (TIMER2_OVF_vect)      // Interrupt Service Rountine Timer Overflow 2
66
67
{
68
  --LDRtick;          // LDRtick    18000*0,1111ms =>  2s bis Leuchtstärke abgefragt wird
69
70
  if(LDRtick==0)        // wenn 2s vergangen
71
72
  {
73
    LDRtick=18000;
74
    //Leuchtstaerke();    // Unterprogramm Leuchtstaerkenbestimmung
75
    GO = 1;
76
  }
77
}
78
79
80
//=================================================================================================
81
82
// Initialisierung LDR=============================================================================
83
84
85
void initLDR()
86
87
{
88
  ADMUX |= 0xC5;    // (1<<REFS1) | (1<<REFS0) | (0<<MUX3) | (1<<MUX2) | (0<<MUX1) | (1<<MUX0)
89
90
            // 1.1V mit Externen Kondensator (REFS) + ADC5 Eingang LDR (MUX)
91
92
  ADCSRA |= 0x87;    // (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
93
94
            // ADC aktivieren + Vorteiler 128 (Prescaler Selection Bits)
95
}  
96
97
98
99
//=================================================================================================
100
101
// Leuchtstärkebestimmung==========================================================================
102
103
104
void Leuchtstaerke()        // ADC= (Vin*1024)/(Vref)
105
106
{
107
  
108
  ADCSRA |= (1<<ADEN);      // AD aktivieren
109
  ADCSRA |= (1<<ADSC);      // Konvertierung starten
110
111
  while (ADCSRA & (1<<ADSC))    // Wenn Konvertierung gestartet ist
112
    
113
  {
114
    ;  
115
  }
116
117
  lux = ADCL;            // Erst ADCL aus Register lesen
118
  lux += (ADCH<<8);        // dann ADCH, weil sonst ADC Data Register nicht geupdated wird
119
  lux=0;
120
  
121
  for (i=0; i<4; i++)
122
  
123
  {
124
    ADCSRA |= (1<<ADSC);
125
126
    while (ADCSRA & (1<<ADSC))  // Wenn Konvertierung gestartet ist
127
  
128
    {
129
      ;  
130
    }
131
132
    lux = ADCL;
133
    lux += (ADCH<<8);      // ADCH aufsummieren
134
  }
135
    
136
  ADCSRA &= ~(1<<ADEN);      // Konvertierung beenden
137
  
138
  lux /=4;            // durch 4 teilen
139
  wert = lux;            
140
}

Dank euch schonmal!

von Hc Z. (mizch)


Lesenswert?

Mir fehlt eine Beschreibung, was denn nicht geht.  Außer, dass es vorher 
funktioniert habe, finde ich darüber nichts.  Somit lautet die 
Fehlerbeschreibung wohl „funktioniert nicht“.  Das ist etwas mager. 
Irgendwie wäre es schon schön, zu wissen, nach welchem Fehler man suchen 
soll.

von Peter D. (peda)


Lesenswert?

Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Mach mal ein Loch in Deinen Kopf, damit wir reingucken können, an 
welchen MC Du gerade denkst.

Bei den AVRs braucht der T2 im asynchronen Mode unbedingt den 32kHz 
Quarz!

Müssen es denn unbedingt exakt 2s sein?
Für ungefähr 2s nimm doch einfach den Watchdoginterrupt.


Peter

von Alex (Gast)


Lesenswert?

okay sorry ^^ stimmt ein paar infos fehlen wirklich :)

also es handelt sich um einen ATmega88

Achso für Asynchronen Modus brauch man zwingen den 32khz? okay das 
wusste ich nicht :)
noch ne frage dazu... wieso muss er eigentlich unbedingt asynchron 
laufen?

also später möchte ich mit dem 32khz quarz eigentlich nur alle paar 
minuten, vllt so 10min, eine Messung vornehmen. kann ich dafür auch den 
watchdog verwenden? ich dachte bei dem watchdog wacht er dann aus und 
beginnt das programm von neuem, also als ob ein reset ausgeführt wurde 
oder bin ich da falsch informiert ^^

Ok ... also Loch reinbohr!

Problem ist dieses... die LDR geht nun garnicht mehr an. Davor hatte ich 
die Abfrage der Werte außerhalb der if(GO=1) schleife, da ging 
wenigstens die LED, jetzt wo sie innerhalb ist geht nichts mehr :( und 
ich seh den fehler nicht!

von Hc Z. (mizch)


Lesenswert?

Der Watchdog im Mega88 kann auch Interrupts auslösen und damit 
aufwecken.  Ein auf Interrupt konfigurierter Watchdog wird, wenn er beim 
Interrupt nicht neu bedient wird, erst im zweiten Anlauf einen Reset 
auslösen.  Maximale Zeit ist beim Mega88 8 Sekunden.

von Alex (Gast)


Lesenswert?

oh ja ;) seh ich grad

das ja cool... das heißt er wacht alle 8 sekunden auf wenn WDIF gesetzt 
wird, oder?

Das heißt nun ich baue meine ISR(POWER_SAVE_MODE) ganz normal auf und 
der Watchdog ruft diese alle 8s auf, weil dann ja ein Interrupt eintritt 
oder?

Ich muss dann in den nächsten 8 Sekunden den Watchdog resetten, damit er 
nicht einen kompletten Reset des Systems hervorruft. Hab ich das so 
richtig verstanden?

Den Reset kann ich dann in der Hauptschleife setzen oder auch in der 
ISR, wenn ich z.b. will, das mein Programm erst nach 80s eine Aktion 
ausführt, setze ich ihn die ISR damit er mir dort eine Variable bis 10 
hochzählt, bis er mir dann in das eingentliche Hauptprogramm springt...

von Hc Z. (mizch)


Lesenswert?

Das ist das Prinzip, ja.  Die ISR für den Watchdog-Interrupt muss 
allerdings ISR(WDT_vect) heißen, wenn ich das richtig sehe.  Ich würde 
in der ISR eine Variable herunterzählen (und WDIE setzen) -- sonst 
nichts -- und im Hauptprogramm solange schlafen, bis die Variable 0 ist. 
Dann messen und die Variable neu setzen.

von Alex (Gast)


Lesenswert?

Ist WDIE das gleiche wie z.b. wdt_enable(WDTO_8S);  ?

Das WDIW muss ich dann in der ISR setzen oder in der while(1)-Schleife 
vom main-Programm?

von Hc Z. (mizch)


Lesenswert?

Alex schrieb:
> Ist WDIE das gleiche wie z.b. wdt_enable(WDTO_8S);  ?

Nein.  Im Datenblatt steht (in C und Asm) ein Beispiel, wie Du den 
Watchdog beschreibst.  Du musst eine bestimmte Sequenz einhalten.  Die 
dafür nötigen Registerbits stehen auch in diesem Datenblatt (Beispiel 
kurz vor, Registerbits in Abschnitt 8.9.2).

Alex schrieb:
> Das WDIW muss ich dann in der ISR setzen oder in der while(1)-Schleife
> vom main-Programm?

Ich nehme an, Du meinst WDIE.  Das solltest Du direkt in der 
Interrupt-Routine neu setzen (mit der oben erwähnten Sequenz) und sonst 
(außer dem Herunterzählen des Counters) nicht viel machen.  Um so 
schneller ist das Hauptprogramm wieder dran, das die meiste Zeit gleich 
in den nächsten sleep() geht.

von Alex (Gast)


Lesenswert?

werden diese nicht über die wdt.h mit diesem befehl gesetzt? oder bin 
ich da schief gewickelt?
und wdt_reset(); resettet doch soweit ich das richtig in der 
header-datei nachvollziehen konnte den Watchdog?
aber das mit dem reset in der ISR werd ich gerne übernehmen :)

Das programm springt doch, nachdem es aufgeweckt wurde, sofort hinter 
den sleep-befehl, als dahin, wo es zuvor schlafen gegangen ist oder? es 
fängt ja nicht wieder von vorne an...

von Hc Z. (mizch)


Lesenswert?

wdt_reset() setzt nur den Watchdog-Timer zurück.  Es beeinflusst keine 
Flags.  Am besten, Du schaust Dir im Datenblatt die Beschreibung zu WDIE 
an.  Soweit ich mich erinnere, muss das bei jedem Watchdog-Interrupt neu 
gesetzt werden, sonst löst der Watchdog beim nächsten Mal einen Reset 
aus.

Nachdem aufgeweckt wurde, arbeitet das Programm zunächst einmal den 
Interrupt ab.  Wie immer beim Interrupt wird nach dessen Erledigung das 
Hauptprogramm dort fortgesetzt, wo es unterbrochen wurde -- in diesem 
Fall nach dem sleep().  Dort prüfst Du, ob der Zähler seinen Endwert 
erreicht hat und, wenn nicht, gehst in den nächsten sleep().

von Alex (Gast)


Lesenswert?

So ich hab jetzt hier probehalber nen Code geschrieben, welcher die LED 
für eine Sekunde zum Leuchten bringen soll, nachdem der IC aufgeweckt 
wird.

Habe mal deine Vorschläge befolgt, allerdings leuchtet die LED nicht und 
er scheint auch garnicht in den Sleep Mode zu gehen :( Vllt wisst ihr wo 
mein Fehler noch ist... Hoff ich hab die FUnktion ausreichend 
kommentiert!
1
int main(void)
2
3
{
4
  WDTCSR |= 0x48;      // setzt WDIE und WDE für Interrupt and System Reset Mode
5
6
  wdt_enable(WDTO_4S);  // Watchdog auf 4Sekunden bis zum Auslösen
7
8
  sei();          // Interrupts aktivieren
9
    
10
  while(1)
11
  
12
  {  
13
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
14
        sleep_mode();       // in den Schlafmodus wechseln
15
16
    WDTCSR |= (1<<WDIE);// WDT Interrupt Flag erneut setzen, damit kein Reset erfolgt
17
18
    if (GO==1)
19
    
20
    {            
21
      SET_BIT(DDRB, DDB2);            
22
      CLR_BIT(PORTB, PB2);    // LED einschalten
23
24
      _delay_ms(1000);
25
26
      GO=0;
27
    }
28
  }      
29
}
30
31
32
//================================================================================================
33
34
//Watchdog Interrupt==============================================================================
35
36
ISR (WDT_vect)      // Interrupt Service Rountine Timer Overflow 2
37
38
{
39
  GO=1;
40
  SET_BIT(DDRB, DDB2);
41
  SET_BIT(PORTB, PB2);    // LED ausschalten, falls an
42
}

Danke euch schonmal :)

von Stefan B. (stefan) Benutzerseite


Lesenswert?

>   WDTCSR |= 0x48;      // setzt WDIE und WDE für Interrupt and System Reset Mode

Das ist so nicht erlaubt. Nur das WDIE Bit kann direkt gesetzt werden. 
Die Änderung des WDE Bits muss eine strenge Sequenz einhalten, die in 
wdt_enable() bzw. wdt_disable() vorexerziert wird.

von Alex (Gast)


Lesenswert?

Wenn ich nun aber wdt_enable(4s) setze... dann ist doch noch nicht 
gesagt ob er jetzt im System Reset Mode oder Interrupt Mode oder ... 
läuft? da wird doch das WDE-Bit garnicht gesetzt! Ich kann leider auch 
mit diesen Assembler-Befehlen nicht viel anfangen! :(

von Stefan B. (stefan) Benutzerseite


Lesenswert?

wdt_enable ist in avr/wdt.h als Inline Assembler Makro definiert.

In dem wdt_enable Code werden das WDE Bit plus die Prescalerwerte (hier 
passend zu WDTO_4S) gesetzt, d.h es wird der System Reset Mode 
eingestellt. Inline Assembler ist nicht einfach lesbar, aber das Setzen 
des WDE erkennt man.

Das WDIE Bit würde ich (bzw. habe es im Demoprogramm) danach dazuodern. 
Das geht mit Standard-C ohne Inline Assembler.

Dein Atmega88-Programm kann ich erst heute abend auf meinem Board testen 
(wenn es auf Attiny2313 übertragbar ist). Im Attiny2313 Datenblatt ist 
in der Tabelle mit den verschiedenen Modi auch noch die WDTON Fuse 
aufgeführt. Bei meinen Experimenten mit dem Attiny2313 war es allerdings 
nicht nötig die WDTON Fuse zu programmieren.

von Alex (Gast)


Lesenswert?

okay... LED fängt nun nach 4s an zu leuchten... leuchtet aber dauerhaft!

hab nun WDTCSR |= 0x48; entfernt und nach wdt_enable(); noch WDTCSR |= 
(1<<WDIE) gesetzt. Das sollte mir doch nun den Reset und Interrupt Mode 
geben, oder?

Habe dann WDTCSR |= (1<<WDIE) nochmals nach dem Sleep Befehlt gesetzt, 
damit das Bit nach dem Interrupt gleich wieder gesetzt wird!

Was ist daran noch falsch? Muss ich zufällig noch wdt_reset(); benutzen?

von Alex (Gast)


Lesenswert?

Hab grad das zweite WDTCSR |= (1<<WDIE) durch wdt_reset(); ersetzt und 
nun leuchtet die LED nach 4s und bleibt schätzungsweise 5s an und ist 
dann wieder für 4s aus... wieso das?

ich hab die delay auf 1000ms gestellt, also sollte die doch 1s leuchten 
und anschließend wieder in den sleep-modus gehen. sollte die dann nicht 
ausgehen, wenn er dort reingeht?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Nee. Der Sleep Modus ändert hier nix an den Ports. Wenn du Ports im 
Sleep Modus abschalten willst, um Strom zu sparen, musst du das selbst 
machen. Vor dem Aufruf des Sleep.

von Alex (Gast)


Lesenswert?

hab nun mal den Stromverbrauch meines ICS gemessen...

also laut dem multimeter verbraucht er lange nur 4uA, geht dann ganz 
kurz auf 7mA hoch und wieder zurück auf 4uA...

Also den Sleep_Modus wird er wohl machen, nur das die LED 4-5s leuchtet 
verstehe ich noch nicht ;(

Im Grunde ists doch so:

Initialisierung
.
Sleepmodus
.
WD
.
ISR -> GO=1
.
LED an, da GO=1
.
1s warten, dann GO=0
.
Sleepmodus
.
WD
.
ISR -> GO=1 und zusätzlich LED aus!!!
.
LED an, da GO=1
.
.
.

Das bedeutet eigentl sie müsste an gehen und sobald sie in den interrupt 
gelangt ausgehen um dann danach sofort wieder anzugehen. so könnte doch 
niemals eine auszeit von 4s auftreten oder?

von Alex (Gast)


Lesenswert?

Sorry wird immer länger hier ;P

hab jetzt alles hinbekommen soweit ;)

Die LED leuchtet kurz und hört dann wieder auf... jetzt nochmal 
abschließend:

Mit   WDTCSR |= (1<<WDIE)    setz ich das Bit, damit der WD nach dem 
2.Durchlauf keinen System Reset durchführt, stimmts?

Und mit wdt_reset(); kann ich den 4s Timer wieder auf anfang setzen 
oder?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

2x JA!

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.