www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AVR Studio Simulationsmacken und Verständnisfrage


Autor: Stephan Mustermann (johnwurst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich habe mit einem Attiny13 ein frei programmierbares 
Glühzeitsteuergerät gebaut, welches soweit auch schon ganz gut 
funktioniert. Bis auf die Tatsache, dass ich scheinbar einen Kurzschluss 
produziere, sobald ich 5V auf den INT0 Pin gebe. Hat jemand eine Ahnung 
wieso? Muss ich den zusätzlich als Eingang schalten?

Außerdem scheint AVR Studio mit meinem Code Probleme zu haben. Die 
Schaltung funktioniert (bis auf die Sache mit dem INT0), wenn ich es 
simuliere im AVR Studio läuft der Timer aber aus irgendeinem Grund nicht 
los. Jemand eine Ahnung warum?

So, hier noch der Code. Die Glühzeiten geben natürlich noch keinen Sinn, 
aber eins nach dem anderen....

Gruß und Danke


#include <avr/io.h>
#include <avr/interrupt.h>

          

///////////////////////////////////////////////////
// Global variables

volatile uint8_t overflow_flag = 0;  //timer ovf Flag
volatile uint8_t ng_flag = 0; //nachglüh Flag
uint16_t counter = 0;
uint16_t vg_end = 0;
uint16_t ng_end = 0;
volatile uint16_t adc_value = 0;




//////////////////////////////////////////////////////
// Main Programm
int main(void)
{


// Input/Output Ports initialization
// Port B initialization

PORTB=0x00;
DDRB |= (1<<PB2); //PB2 auf Ausgang


//Interrupt (Klemme 50 = Anlassen) initialisieren
MCUCR |= (1<<ISC01)|(1<<ISC00); //Rising Edge triggered
GIMSK |= (1<<INT0); //Interrupt enable

//ADC initialisieren

ADCSRA |=  (1<<ADEN)|(1<<ADPS1)|(1<<ADPS2); //ADC enable, prescaler 64
ADMUX |= (1<<ADLAR)|(1<<MUX1)|(1<<MUX0); //left adjust, PB3 = ADC Port


ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
 while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
    ;
adc_value = ADCH;
adc_value = 0; //messung verwerfen, da die erste messung ungenau ist
 


// Timer/Counter 0 initialization

TCCR0A |= (1<<WGM01); //ctc mode
OCR0A = 150;
TCCR0B = (1<<CS00)|(1<<CS02); //Prescaler =  und Timer starten 
TIMSK0 |= (1<<OCIE0A); //Output Compare Interrupt

sei();         // Global enable interrupts





//4x ADC auslesen und Mittelwert bilden

/* ADC Mehrfachmessung mit Mittelwertbbildung */

for (uint8_t i = 0; i < 4; ++i ){

    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
        ;
    adc_value += ADCH;
     } //End for(...)
    
adc_value = adc_value/4;



//fallunterscheidung - glühzeiten in abhängigkeit vom ADC Wert, vg_end = 64 entspricht einer Sekunde
if(adc_value > 230) {
    vg_end = 640; //entspricht sek. glühen
    ng_end = 300;  //entspricht sek. nachglühen
  }

else if(adc_value > 150) {
    vg_end = 15; //entspricht sek. glühen
    ng_end = 15;  //entspricht sek. nachglühen
    }

else if(adc_value > 0) {
    vg_end = 15; //
    ng_end = 15;  //
}



PORTB |= (1<<PB2); //Glühkerzen einschalten
                   //TODO Glüh-LED einschalten


for (;;) {     // Endlos Schleife      
          
       
      ////vorglühtimer
      ////////////////
      if((overflow_flag == 1)&&(ng_flag == 0))
           {     
           overflow_flag = 0; //zurücksetzen
           counter++;
            
           

               if(counter == vg_end)   
               {                 
               counter = 0;     
               PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
                             //TODO LED ausschalten
               }
           
           } //ende if(overflow...)  
        
           

       ////nachglühtimer
       /////////////////
      if((overflow_flag == 1)&&(ng_flag == 1))
       {     
       PORTB |= (1<<PB2); //Glühkerzen einschalten
           overflow_flag = 0; //zurücksetzen
           counter++;


                               //TODO Vorglüh-LED Blinker einfügen

           
               if(counter == ng_end)   
               {  
         PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
                             //TODO Vorglüh-LED ausschalten               
               counter = 0;     
         TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
               
               }
           
           } //ende if(overflow...)  
        



       

      }//ende for()
} //ende main()


//////////////////////////////////////////////////////////////
///////// ISR Routinen


ISR(TIM0_COMPA_vect)
{    
  overflow_flag = 1;      
}




ISR(INT0_vect)
{


  if(ng_end >0) { //nachglühen erforderlich
   counter = 0; //zähler zurücksetzen
  
   ng_flag = 1;

  }


  else { //es muss nicht nachgeglüht werden
   TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
   PORTB &= ~(1<<PB2);               //Glühkerzen ausschalten
  }


}






Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stephan Mustermann schrieb:

> Bis auf die Tatsache, dass ich scheinbar einen Kurzschluss
> produziere, sobald ich 5V auf den INT0 Pin gebe. Hat jemand eine Ahnung
> wieso? Muss ich den zusätzlich als Eingang schalten?

Du benutzt den Pin doch als Eingang (Spannung kommt von außen). Also 
macht die Konfiguration als Eingang auch Sinn.

Es macht IMHO auch Sinn die 5V von außen über einen Serienwiderstand 
(>5kOhm => <1mA) auf den Pin zu geben für den Fall, dass die externe 
Schaltung läuft, der Attiny13 aber noch keinen Saft hat. Ohne 
Serienwiderstand können die 5V die internen Schutzdioden rösten.

> Außerdem scheint AVR Studio mit meinem Code Probleme zu haben. Die
> Schaltung funktioniert (bis auf die Sache mit dem INT0), wenn ich es
> simuliere im AVR Studio läuft der Timer aber aus irgendeinem Grund nicht
> los. Jemand eine Ahnung warum?

Wenn ich in der Simulation dafür sorge, dass der Simulator über diese 
Stellen (2x) kommt:

  while (ADCSRA & (1<<ADSC) ) // auf Abschluss der Konvertierung warten
    ;

wird auch die ISR(TIM0_COMPA_vect) periodisch aufgerufen.

Der Simulator simuliert keinen echten ADC, das Bit ADSC (0: Wandlung 
fertig) wird vom Simulator nicht zurück gesetzt und daher ist das 
Codestück eine Endlosschleife!

Autor: Stephan Mustermann (johnwurst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan B. schrieb:
> Du benutzt den Pin doch als Eingang (Spannung kommt von außen). Also
> macht die Konfiguration als Eingang auch Sinn.

Ok, das klingt sinnvoll. Ist mir allerdings neu. Ich hatte die Ports nie 
zusätzlich als Eingang definiert. Das sollte ich dann wohl bei ADC und 
INT0 nachholen. Gibt es irgendwelche Nachteile, wenn man das nicht tut?

> Es macht IMHO auch Sinn die 5V von außen über einen Serienwiderstand
> (>5kOhm => <1mA) auf den Pin zu geben für den Fall, dass die externe
> Schaltung läuft, der Attiny13 aber noch keinen Saft hat. Ohne
> Serienwiderstand können die 5V die internen Schutzdioden rösten.

Auch das werde ich nachholen. Wieviel Strom kann ein Attiny Pin 
eigentlich liefern / durch sich fließen lassen?


> Wenn ich in der Simulation dafür sorge, dass der Simulator über diese
> Stellen (2x) kommt:
>
>   while (ADCSRA & (1<<ADSC) ) // auf Abschluss der Konvertierung warten
>     ;
>
> wird auch die ISR(TIM0_COMPA_vect) periodisch aufgerufen.
>
> Der Simulator simuliert keinen echten ADC, das Bit ADSC (0: Wandlung
> fertig) wird vom Simulator nicht zurück gesetzt und daher ist das
> Codestück eine Endlosschleife!

Das mit dem ADSC Bit hatte ich auch gemacht. Und bin auch bis zu der 
Stelle gekommen, wo er eigentlich anfangen sollte, TCNT hochzuzählen um 
damit dann irgendwann das Flag zu setzen. Er beginnt aber garnicht erst 
zu zählen, obwohl in der Sidebar "running, prescaler 1024" steht. 
Scheint irgendein Fehler mit meinem AVR Studio zu sein.

Werde die Tips mal umsetzen und mich dann wieder melden.

Grüße und Danke!

Autor: Stephan Mustermann (johnwurst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

habe die Tipps umgesetzt. Der Kurzschluss ist wohl entstanden, weil ich 
Depp die 5V auf einen Ausgang gegeben habe, der auf 0 lag. Man sollte 
eben die Pin Belegung richtig lesen...
Mein Programm funktioniert jetzt auch soweit eigentlich. Aber irgendwas 
läuft mit dem Timer schief. Die Vorglühzeit stimmt, er glüht 10 
Sekunden. Aber wenn ich mit einem Interrupt (ich tippe mit einem Kabel 
über einen Widerstand an den INT0 Pin) die Nachglühphase einleite 
schwankt die Zeit zwischen 1 und 3 Sekunden (sollte 5 Sek. sein) Wie 
kann das sein?

#include <avr/io.h>
#include <avr/interrupt.h>

          

///////////////////////////////////////////////////
// Global variables

volatile uint8_t overflow_flag = 0;  //timer ovf Flag
volatile uint8_t ng_flag = 0; //nachglüh Flag
uint16_t counter = 0;
uint16_t vorglueh_end = 0;
uint16_t nachglueh_end = 0;
volatile uint16_t adc_value = 0;




//////////////////////////////////////////////////////
// Main Programm
int main(void)
{


// Input/Output Ports initialization
// Port B initialization

PORTB=0x00;
DDRB |= ((1<<PB2)|(1<<PB0)); //PB2 auf Ausgang (glüherzen), PB0 Ausgang LED
DDRB &= ~((1<<PB1)|(1<<PB3)); //PB0, PB3 auf Eingang (ADC3, INT0)


//Interrupt0 (Klemme 50 = Anlassen) initialisieren
MCUCR |= (1<<ISC01)|(1<<ISC00); //Rising Edge triggered
GIMSK |= (1<<INT0); //Interrupt enable

//ADC initialisieren

ADCSRA |=  (1<<ADEN)|(1<<ADPS1)|(1<<ADPS2); //ADC enable, prescaler 64
ADMUX |= (1<<ADLAR)|(1<<MUX1)|(1<<MUX0); //left adjust, PB3 = ADC Port


ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
 while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
    ;
adc_value = ADCH;
adc_value = 0; //messung verwerfen, da die erste messung ungenau ist
 


// Timer/Counter 0 initialization

TCCR0A |= (1<<WGM01); //ctc mode
OCR0A = 150;
TCCR0B = (1<<CS00)|(1<<CS02); //Prescaler =  und Timer starten 
TIMSK0 |= (1<<OCIE0A); //Output Compare Interrupt

sei();         // Global enable interrupts





//4x ADC auslesen und Mittelwert bilden

/* ADC Mehrfachmessung mit Mittelwertbbildung */

for (uint8_t i = 0; i < 4; ++i ){

    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
    while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
        ;
    adc_value += ADCH;
     } //End for(...)
    
adc_value = adc_value/4;



//fallunterscheidung - glühzeiten in abhängigkeit vom ADC Wert, vg_end = 64 entspricht einer Sekunde
if(adc_value > 230) {
    vorglueh_end = 640; //entspricht 10sek. glühen
    nachglueh_end = 320;  //entspricht 5sek. nachglühen
  }





PORTB |= (1<<PB2); //Glühkerzen einschalten
                   //TODO Glüh-LED einschalten


for (;;) {     // Endlos Schleife      
          
       
      ////vorglühtimer
      ////////////////
      if((overflow_flag == 1)&&(ng_flag == 0))
           {     
           overflow_flag = 0; //zurücksetzen
           counter++;
            
           

               if(counter == vorglueh_end)   
               {                 
               counter = 0;     
               PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
                             //TODO LED ausschalten
               }
           
           } //ende if(overflow...)  
        
           

       ////nachglühtimer
       /////////////////
      if((overflow_flag == 1)&&(ng_flag == 1))
       {     
       
           overflow_flag = 0; //zurücksetzen
           counter++;


                               //TODO Vorglüh-LED Blinker einfügen

           
               if(counter == nachglueh_end)   
               {  
         PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
                             //TODO Vorglüh-LED ausschalten               
               counter = 0;     
         TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
         cli(); //interrupts ausschalten

               }
           
           } //ende if(overflow...)  
        



       

      }//ende for()
} //ende main()


//////////////////////////////////////////////////////////////
///////// ISR Routinen


ISR(TIM0_COMPA_vect)
{    
  overflow_flag = 1;      
}




ISR(INT0_vect)
{
 
  if(nachglueh_end==0) { //nachglühen nicht erforderlich
   TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
   PORTB &= ~(1<<PB2);               //Glühkerzen ausschalten
  }


  else { //es muss nachgeglüht werden
   PORTB |= (1<<PB2); //Glühkerzen einschalten
   counter = 0; //zähler zurücksetzen
   ng_flag = 1;
  }

}



Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

sollte Counter nicht auch volatile sein, wo doch dieser in der ISR(INT0) 
geändert wird?!

Sascha

Autor: Stephan Mustermann (johnwurst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
super gut! Das war der Fehler! Da wäre ich in 10Jahren nicht drauf 
gekommen. Wo dran lag das denn jetzt genau? Ich gehe mal davon aus, dass 
der Compiler irgendwie die Variable wegoptimiert hat oder?

Allgemein gilt: Alles was in Interrupt Routinen geändert wird, sollte 
volatile sein ?

Grüße

Autor: Sascha Weber (sascha_w)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stephan Mustermann schrieb:
> Wo dran lag das denn jetzt genau? Ich gehe mal davon aus, dass
> der Compiler irgendwie die Variable wegoptimiert hat oder?
das nicht, aber ohne volatile merken die Funktion außerhalb der ISR u.u. 
nicht sofort eine Veränderung.
Bei dir lag es sicher an folgender Stelle:
wird die Ausführung von
counter++;
durch die ISR unterbrochen, so wird zwar innerhalb der ISR der Wert auf 
0 gesetzt, aber außerhalb wird evl. mit dem alten Wert weitergerechnet, 
da Counter++ ja aus mehreren ASM-Befehlen besteht.

> Allgemein gilt: Alles was in Interrupt Routinen geändert wird, sollte
> volatile sein ?
zumindest wenn die Variable von "beiden Seiten" verändert wird.

EDIT: siehe auch im Tutorial: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Sascha

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.