mikrocontroller.net

Forum: Compiler & IDEs AD-Wandlung + Power SAVE


Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?


int main(void)

{
  //initLCD_Timer2();        // Initialiserung LCD und Timer2 OVF
  
    ASSR  = (1<< AS2);              // Timer2 asynchron takten
    _delay_ms(1000);                 // Einschwingzeit des 18MHz Quarzes
    TCCR2B  = 2;                    // Vorteiler 8 -> 0,1111ms Überlaufperiode
    while((ASSR & (1<< TCR2BUB)));  // Warte auf das Ende des Zugriffs
    TIFR2   = (1<<TOV2);            // Interrupts löschen (*)
    TIMSK2 |= (1<<TOIE2);           // Timer overflow Interrupt freischalten

  initLDR();              // LDR initialiseren
  
  ACSR = 0x80;            // AD ausschalten
  
  sei();                // Interrupts aktivieren
    
  while(1)
  
  {  
        OCR2B = 0;                       // Dummyzugriff
        while((ASSR & (1<< OCR2BUB)));   // Warte auf das Ende des Zugriffs
 
        set_sleep_mode(SLEEP_MODE_PWR_SAVE);
        sleep_mode();                   // in den Schlafmodus wechseln

    if (GO==1)
    
    {      
      cli();
      ACSR &= ~(1<<ACD);        // A/D aktivieren
      Leuchtstaerke();
      GO=0;
    

      if (wert>=240)
      {
        SET_BIT(DDRB, DDB2);            
        CLR_BIT(PORTB, PB2);    // LED einschalten, falls an
        _delay_ms(1000);
        sei();  
      }  


      if (wert<240)

      {
        SET_BIT(DDRB, DDB2);
        SET_BIT(PORTB, PB2);    // LED ausschalten, falls an
        sei();
      }
    }
  }      
}


//=================================================================================================

// Interrupt Routine===============================================================================

// Interrupt Timer 2 (8bit) 18432000/256=72000hz  / pre8 = 9000hz  => 0,11111ms/OVF


ISR (TIMER2_OVF_vect)      // Interrupt Service Rountine Timer Overflow 2

{
  --LDRtick;          // LDRtick    18000*0,1111ms =>  2s bis Leuchtstärke abgefragt wird

  if(LDRtick==0)        // wenn 2s vergangen

  {
    LDRtick=18000;
    //Leuchtstaerke();    // Unterprogramm Leuchtstaerkenbestimmung
    GO = 1;
  }
}


//=================================================================================================

// Initialisierung LDR=============================================================================


void initLDR()

{
  ADMUX |= 0xC5;    // (1<<REFS1) | (1<<REFS0) | (0<<MUX3) | (1<<MUX2) | (0<<MUX1) | (1<<MUX0)

            // 1.1V mit Externen Kondensator (REFS) + ADC5 Eingang LDR (MUX)

  ADCSRA |= 0x87;    // (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)

            // ADC aktivieren + Vorteiler 128 (Prescaler Selection Bits)
}  



//=================================================================================================

// Leuchtstärkebestimmung==========================================================================


void Leuchtstaerke()        // ADC= (Vin*1024)/(Vref)

{
  
  ADCSRA |= (1<<ADEN);      // AD aktivieren
  ADCSRA |= (1<<ADSC);      // Konvertierung starten

  while (ADCSRA & (1<<ADSC))    // Wenn Konvertierung gestartet ist
    
  {
    ;  
  }

  lux = ADCL;            // Erst ADCL aus Register lesen
  lux += (ADCH<<8);        // dann ADCH, weil sonst ADC Data Register nicht geupdated wird
  lux=0;
  
  for (i=0; i<4; i++)
  
  {
    ADCSRA |= (1<<ADSC);

    while (ADCSRA & (1<<ADSC))  // Wenn Konvertierung gestartet ist
  
    {
      ;  
    }

    lux = ADCL;
    lux += (ADCH<<8);      // ADCH aufsummieren
  }
    
  ADCSRA &= ~(1<<ADEN);      // Konvertierung beenden
  
  lux /=4;            // durch 4 teilen
  wert = lux;            
}


Dank euch schonmal!

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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().

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!
int main(void)

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

  wdt_enable(WDTO_4S);  // Watchdog auf 4Sekunden bis zum Auslösen

  sei();          // Interrupts aktivieren
    
  while(1)
  
  {  
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_mode();       // in den Schlafmodus wechseln

    WDTCSR |= (1<<WDIE);// WDT Interrupt Flag erneut setzen, damit kein Reset erfolgt

    if (GO==1)
    
    {            
      SET_BIT(DDRB, DDB2);            
      CLR_BIT(PORTB, PB2);    // LED einschalten

      _delay_ms(1000);

      GO=0;
    }
  }      
}


//================================================================================================

//Watchdog Interrupt==============================================================================

ISR (WDT_vect)      // Interrupt Service Rountine Timer Overflow 2

{
  GO=1;
  SET_BIT(DDRB, DDB2);
  SET_BIT(PORTB, PB2);    // LED ausschalten, falls an
}

Danke euch schonmal :)

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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! :(

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
2x JA!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.