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


von Stephan M. (johnwurst)


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
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
          
5
6
///////////////////////////////////////////////////
7
// Global variables
8
9
volatile uint8_t overflow_flag = 0;  //timer ovf Flag
10
volatile uint8_t ng_flag = 0; //nachglüh Flag
11
uint16_t counter = 0;
12
uint16_t vg_end = 0;
13
uint16_t ng_end = 0;
14
volatile uint16_t adc_value = 0;
15
16
17
18
19
//////////////////////////////////////////////////////
20
// Main Programm
21
int main(void)
22
{
23
24
25
// Input/Output Ports initialization
26
// Port B initialization
27
28
PORTB=0x00;
29
DDRB |= (1<<PB2); //PB2 auf Ausgang
30
31
32
//Interrupt (Klemme 50 = Anlassen) initialisieren
33
MCUCR |= (1<<ISC01)|(1<<ISC00); //Rising Edge triggered
34
GIMSK |= (1<<INT0); //Interrupt enable
35
36
//ADC initialisieren
37
38
ADCSRA |=  (1<<ADEN)|(1<<ADPS1)|(1<<ADPS2); //ADC enable, prescaler 64
39
ADMUX |= (1<<ADLAR)|(1<<MUX1)|(1<<MUX0); //left adjust, PB3 = ADC Port
40
41
42
ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
43
 while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
44
    ;
45
adc_value = ADCH;
46
adc_value = 0; //messung verwerfen, da die erste messung ungenau ist
47
 
48
49
50
// Timer/Counter 0 initialization
51
52
TCCR0A |= (1<<WGM01); //ctc mode
53
OCR0A = 150;
54
TCCR0B = (1<<CS00)|(1<<CS02); //Prescaler =  und Timer starten 
55
TIMSK0 |= (1<<OCIE0A); //Output Compare Interrupt
56
57
sei();         // Global enable interrupts
58
59
60
61
62
63
//4x ADC auslesen und Mittelwert bilden
64
65
/* ADC Mehrfachmessung mit Mittelwertbbildung */
66
67
for (uint8_t i = 0; i < 4; ++i ){
68
69
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
70
    while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
71
        ;
72
    adc_value += ADCH;
73
     } //End for(...)
74
    
75
adc_value = adc_value/4;
76
77
78
79
//fallunterscheidung - glühzeiten in abhängigkeit vom ADC Wert, vg_end = 64 entspricht einer Sekunde
80
if(adc_value > 230) {
81
    vg_end = 640; //entspricht sek. glühen
82
    ng_end = 300;  //entspricht sek. nachglühen
83
  }
84
85
else if(adc_value > 150) {
86
    vg_end = 15; //entspricht sek. glühen
87
    ng_end = 15;  //entspricht sek. nachglühen
88
    }
89
90
else if(adc_value > 0) {
91
    vg_end = 15; //
92
    ng_end = 15;  //
93
}
94
95
96
97
PORTB |= (1<<PB2); //Glühkerzen einschalten
98
                   //TODO Glüh-LED einschalten
99
100
101
for (;;) {     // Endlos Schleife      
102
          
103
       
104
      ////vorglühtimer
105
      ////////////////
106
      if((overflow_flag == 1)&&(ng_flag == 0))
107
           {     
108
           overflow_flag = 0; //zurücksetzen
109
           counter++;
110
            
111
           
112
113
               if(counter == vg_end)   
114
               {                 
115
               counter = 0;     
116
               PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
117
                             //TODO LED ausschalten
118
               }
119
           
120
           } //ende if(overflow...)  
121
        
122
           
123
124
       ////nachglühtimer
125
       /////////////////
126
      if((overflow_flag == 1)&&(ng_flag == 1))
127
       {     
128
       PORTB |= (1<<PB2); //Glühkerzen einschalten
129
           overflow_flag = 0; //zurücksetzen
130
           counter++;
131
132
133
                               //TODO Vorglüh-LED Blinker einfügen
134
135
           
136
               if(counter == ng_end)   
137
               {  
138
         PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
139
                             //TODO Vorglüh-LED ausschalten               
140
               counter = 0;     
141
         TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
142
               
143
               }
144
           
145
           } //ende if(overflow...)  
146
        
147
148
149
150
       
151
152
      }//ende for()
153
} //ende main()
154
155
156
//////////////////////////////////////////////////////////////
157
///////// ISR Routinen
158
159
160
ISR(TIM0_COMPA_vect)
161
{    
162
  overflow_flag = 1;      
163
}
164
165
166
167
168
ISR(INT0_vect)
169
{
170
171
172
  if(ng_end >0) { //nachglühen erforderlich
173
   counter = 0; //zähler zurücksetzen
174
  
175
   ng_flag = 1;
176
177
  }
178
179
180
  else { //es muss nicht nachgeglüht werden
181
   TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
182
   PORTB &= ~(1<<PB2);               //Glühkerzen ausschalten
183
  }
184
185
186
}

von Stefan B. (stefan) Benutzerseite


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:

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

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!

von Stephan M. (johnwurst)


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!

von Stephan M. (johnwurst)


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?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
          
5
6
///////////////////////////////////////////////////
7
// Global variables
8
9
volatile uint8_t overflow_flag = 0;  //timer ovf Flag
10
volatile uint8_t ng_flag = 0; //nachglüh Flag
11
uint16_t counter = 0;
12
uint16_t vorglueh_end = 0;
13
uint16_t nachglueh_end = 0;
14
volatile uint16_t adc_value = 0;
15
16
17
18
19
//////////////////////////////////////////////////////
20
// Main Programm
21
int main(void)
22
{
23
24
25
// Input/Output Ports initialization
26
// Port B initialization
27
28
PORTB=0x00;
29
DDRB |= ((1<<PB2)|(1<<PB0)); //PB2 auf Ausgang (glüherzen), PB0 Ausgang LED
30
DDRB &= ~((1<<PB1)|(1<<PB3)); //PB0, PB3 auf Eingang (ADC3, INT0)
31
32
33
//Interrupt0 (Klemme 50 = Anlassen) initialisieren
34
MCUCR |= (1<<ISC01)|(1<<ISC00); //Rising Edge triggered
35
GIMSK |= (1<<INT0); //Interrupt enable
36
37
//ADC initialisieren
38
39
ADCSRA |=  (1<<ADEN)|(1<<ADPS1)|(1<<ADPS2); //ADC enable, prescaler 64
40
ADMUX |= (1<<ADLAR)|(1<<MUX1)|(1<<MUX0); //left adjust, PB3 = ADC Port
41
42
43
ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
44
 while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
45
    ;
46
adc_value = ADCH;
47
adc_value = 0; //messung verwerfen, da die erste messung ungenau ist
48
 
49
50
51
// Timer/Counter 0 initialization
52
53
TCCR0A |= (1<<WGM01); //ctc mode
54
OCR0A = 150;
55
TCCR0B = (1<<CS00)|(1<<CS02); //Prescaler =  und Timer starten 
56
TIMSK0 |= (1<<OCIE0A); //Output Compare Interrupt
57
58
sei();         // Global enable interrupts
59
60
61
62
63
64
//4x ADC auslesen und Mittelwert bilden
65
66
/* ADC Mehrfachmessung mit Mittelwertbbildung */
67
68
for (uint8_t i = 0; i < 4; ++i ){
69
70
    ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
71
    while (ADCSRA & (1<<ADSC) )     // auf Abschluss der Konvertierung warten
72
        ;
73
    adc_value += ADCH;
74
     } //End for(...)
75
    
76
adc_value = adc_value/4;
77
78
79
80
//fallunterscheidung - glühzeiten in abhängigkeit vom ADC Wert, vg_end = 64 entspricht einer Sekunde
81
if(adc_value > 230) {
82
    vorglueh_end = 640; //entspricht 10sek. glühen
83
    nachglueh_end = 320;  //entspricht 5sek. nachglühen
84
  }
85
86
87
88
89
90
PORTB |= (1<<PB2); //Glühkerzen einschalten
91
                   //TODO Glüh-LED einschalten
92
93
94
for (;;) {     // Endlos Schleife      
95
          
96
       
97
      ////vorglühtimer
98
      ////////////////
99
      if((overflow_flag == 1)&&(ng_flag == 0))
100
           {     
101
           overflow_flag = 0; //zurücksetzen
102
           counter++;
103
            
104
           
105
106
               if(counter == vorglueh_end)   
107
               {                 
108
               counter = 0;     
109
               PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
110
                             //TODO LED ausschalten
111
               }
112
           
113
           } //ende if(overflow...)  
114
        
115
           
116
117
       ////nachglühtimer
118
       /////////////////
119
      if((overflow_flag == 1)&&(ng_flag == 1))
120
       {     
121
       
122
           overflow_flag = 0; //zurücksetzen
123
           counter++;
124
125
126
                               //TODO Vorglüh-LED Blinker einfügen
127
128
           
129
               if(counter == nachglueh_end)   
130
               {  
131
         PORTB &= ~(1<<PB2); //Glühkerzen ausschalten
132
                             //TODO Vorglüh-LED ausschalten               
133
               counter = 0;     
134
         TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
135
         cli(); //interrupts ausschalten
136
137
               }
138
           
139
           } //ende if(overflow...)  
140
        
141
142
143
144
       
145
146
      }//ende for()
147
} //ende main()
148
149
150
//////////////////////////////////////////////////////////////
151
///////// ISR Routinen
152
153
154
ISR(TIM0_COMPA_vect)
155
{    
156
  overflow_flag = 1;      
157
}
158
159
160
161
162
ISR(INT0_vect)
163
{
164
 
165
  if(nachglueh_end==0) { //nachglühen nicht erforderlich
166
   TCCR0B &= ~((1<<CS00)|(1<<CS02)); //Timer anhalten
167
   PORTB &= ~(1<<PB2);               //Glühkerzen ausschalten
168
  }
169
170
171
  else { //es muss nachgeglüht werden
172
   PORTB |= (1<<PB2); //Glühkerzen einschalten
173
   counter = 0; //zähler zurücksetzen
174
   ng_flag = 1;
175
  }
176
177
}

von Sascha W. (sascha_w)


Lesenswert?

Hallo,

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

Sascha

von Stephan M. (johnwurst)


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

von Sascha W. (sascha_w)


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
1
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-Tutorial#Datenaustausch_mit_Interrupt-Routinen

Sascha

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.