Forum: Compiler & IDEs Probleme nach Optimierung


von Crashdemon (Gast)


Lesenswert?

Hallo,

habe ein Programm geschrieben das bis jetzt ohne Optimierung (-O0) super 
lief, jetzt muss ich es aber zwecks erweiterung Optimieren, also im Avr 
Studio auf -Os gestellt, jetzt läuft das Programm aber nicht mehr wie 
gewollt. Ist das mehr ein Problem mit einem Fehler im Code oder eher ein 
grundsätzliches Problem, das zuviel optimiert wird?

von michel (Gast)


Lesenswert?

könnte es evtl sein, dass der compiler eine leere schleife 
"wegoptimiert" hat?
meine gelesen zu haben, dass sowas vorkommen kann.

von Oliver (Gast)


Lesenswert?

Oder ein fehlendes "volatile".

Oliver

von Benedikt K. (benedikt)


Lesenswert?

Es liegt zu 99.9% an dem Code. Es war bisher eben nur Zufall, dass es 
funktioniert hat.

von Bernhard M. (boregard)


Lesenswert?

...oder man hat einen GCC 4.3.0-2 (debian), der optimiert schon mal 
uint32_t Operationen (z.B. division) weg...

von Crashdemon (Gast)


Lesenswert?

Also könnte es sein das er folgende Schleife aus der ReadChannel 
Funktion aus dem AVR-GCC Tutorial wegoptimiert?
1
while(!(MyADCSRA & (1 << ADIF))) 
2
{
3
}

was kann ich dagegen tun?

von lkmiller (Gast)


Lesenswert?

MyADCSRA gibts in dem Tutorial nicht!!

Abhilfe zu deinem MyADCSRA:
volatile char MyADCSRA;

von Crashdemon (Gast)


Lesenswert?

oh, was vergessen, das MyADCSRA ist als volatile uint8_t MyADCSRA 
deklariert und in einem Interupt verwurschtelt:
1
ISR(ADC_vect)
2
{
3
  MyADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
4
}

hier noch die komplette ReadChannel Funktion:
1
uint16_t ReadChannel(uint8_t mux)
2
{
3
  uint8_t i;
4
  uint16_t result = 0;
5
6
  MyADCSRA = 0; 
7
8
  ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0) | (1 << ADIE);  
9
10
  ADMUX = mux; // Kanal waehlen
11
12
    ADCSRA |= (1 << ADSC);              
13
    while ( ADCSRA & (1 << ADSC)) 
14
  {
15
    }
16
17
    result |= ADCL + (ADCH << 8); 
18
19
    result = 0; 
20
21
    for(i = 0; i < 64; i++)
22
    {
23
    ADCSRA |= (1 << ADSC); 
24
    MyADCSRA &= ~(1 << ADIF); 
25
26
    while(!(MyADCSRA & (1 << ADIF))) 
27
    {
28
    }
29
          
30
    result += ADCL + (ADCH << 8);  
31
    }
32
33
  ADCSRA &= ~(1 << ADEN); 
34
 
35
    result /= 64; 
36
   return result;
37
}

von Benedikt K. (benedikt)


Lesenswert?

Gibt es irgendeinen Grund für das MyADCSRA anstelle von dem normalen 
ADCSRA ?
PS: ADCL + ADCH kennt der Compiler als ADC

von Crashdemon (Gast)


Lesenswert?

Habe alle MyADCSRA in ADCSRA umgewandelt, problem besteht aber 
weiterhin.
Ich lese ADCL + ADCH getrennt ein weil der Atmega8 den wert der ADC 
Wandlung in zwei register packt, oder liege ich da falsch?

von Jo S. (tigermatze)


Lesenswert?

Die Register mit dem Ergebnis kannst du getrennt auslesen.
Versuch doch mal:
1
while ((ADCSRA & (1 << ADIF))==0);
oder mach ein "nop" in die Schleife:
1
while(!(MyADCSRA & (1 << ADIF))) 
2
{
3
  __asm("NOP");
4
}
Dann sollte diese nicht mehr wegoptimiert werden.

von Crashdemon (Gast)


Lesenswert?

Leider tritt der Fehler immer noch auf, ich werde wohl mal alle 
Interrupt Routinen checken müssen, vermute das sich dort ein Fehler 
befindet.
Werde später mal das ganze c File posten.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn das Teil `volatile' deklariert ist, wird der Zugriff darauf nicht
wegoptimiert.

Der Bug wird also waonders sein.

Wenn ich zu obigem Sourcecodeschnipsel noch
1
#include <avr/io.h>
2
3
volatile uint8_t MyADCSRA;

voranstelle und das Ganze für einen ATmega8 compiliere, erhalte ich:
1
.L4:
2
        sbi 38-0x20,6
3
        lds r24,MyADCSRA
4
        andi r24,lo8(-17)
5
        sts MyADCSRA,r24
6
.L5:
7
        lds r24,MyADCSRA
8
        sbrs r24,4
9
        rjmp .L5
10
        in r18,36-0x20
11
        in r24,37-0x20
12
        clr r25
13
        mov r25,r24
14
        clr r24
15
        add r24,r18
16
        adc r25,__zero_reg__
17
        add r20,r24
18
        adc r21,r25
19
        subi r19,lo8(-(1))
20
        cpi r19,lo8(64)
21
        brne .L4

Gut erkennbar, dass in MyADCSRA zuerst Bit 4 gelöscht wird (das ist
die ominöse -17, entspricht 0xef) und dann auf Bit 4 darin getestet
wird.

von Johannes M. (johnny-m)


Lesenswert?

Jo Sen wrote:
> Die Register mit dem Ergebnis kannst du getrennt auslesen.
Schon, aber sie müssen unbedingt in der richtigen Reihenfolge ausgelesen 
werden (grundsätzlich zuerst ADCL, dann ADCH). Und bei dem oben 
gezeigten Konstrukt ist nicht gewährleistet, dass das auch der Fall ist. 
Dafür gibt es eben in C extra die 16-Bit-Zugriffe über ADC bzw. ADCW, 
bei denen der Compiler das selber in der richtigen Reihenfolge macht.

von Crashdemon (Gast)


Lesenswert?

Ja, die Ergebnissreister werden natürlich in der richtigen Reihenfolge 
ausgelesen:
1
uint16_t x
2
3
x = ADCL;       
4
x += (ADCH << 8);

von Johannes M. (johnny-m)


Lesenswert?

Crashdemon wrote:
> Ja, die Ergebnissreister werden natürlich in der richtigen Reihenfolge
> ausgelesen:
Oben aber nicht (Posting von 13:42)! Und wie gesagt: Spar Dir den ganzen 
Kram und lies das ADCW aus. Da ist alles drin.

von Crashdemon (Gast)


Lesenswert?

So ich poste hier mal den ganzen Code vllt. befindet sich dort ja ein 
fehler der erst durch die optimierung auftritt:
1
#include <math.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <avr/wdt.h>
5
#include <avr/sleep.h>
6
#include <util/delay.h>
7
#include <avr/interrupt.h>
8
9
#define BAUD 9600UL // Baudrate für serielle Kommunikation
10
#define F_CPU 4000000UL // Systemtakt in Hz
11
#define uart_buffer_size 32    
12
#define MYUBRR F_CPU/16/BAUD-1 // Takt für den USART
13
14
char uart_tx_buffer[uart_buffer_size]; // Sendepuffer TX USART
15
unsigned char counter; // Anzeige die Angeschaltet werden soll
16
unsigned char status[2] = {0x01, 0x02}; // Status LED's (Rot, Grün)
17
unsigned char digit[10] = {0x82, 0xF3, 0x4A, 0x62, 0x33, 0x26, 0x06, 0xF2, 0x00, 0x20}; // Bitmaske für die Zahlen
18
volatile unsigned char display[2] = {0x10, 0x08}; // Siebensegmentanzeige (Einer- , Zehnerstelle)
19
volatile unsigned char segment[2];
20
volatile uint8_t uart_tx_flag = 1; // Flag, String komplett gesendet
21
22
uint8_t MyADCSRA;
23
24
ISR(TIMER0_OVF_vect) // Aufruf bei Interrupt von Timer0 (8 Bit)
25
{
26
  PORTC |= 0xFF; // Die bisherige Anzeige ausschalten
27
28
    counter++; // Welches ist die nächste Stelle, um Eins erhöhen
29
30
    counter &= 0x01; // Wenn Stelle 2 erreicht, zurücksetzen
31
32
    PORTD = segment[counter]; // Jeweiligen Wert ausgeben
33
    PORTC = display[counter]; // Jeweilige Stelle einschalten
34
}
35
36
ISR(ADC_vect)
37
{
38
  MyADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
39
}
40
41
ISR(USART_UDRE_vect) 
42
{
43
  static char* uart_tx_p = uart_tx_buffer;   
44
  uint8_t data;
45
 
46
  data = *uart_tx_p++;
47
  
48
  if(data == 0) 
49
  {        
50
        UCSRB &= ~(1<<UDRIE); // UDRE Interrupt ausschalten        
51
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen
52
        uart_tx_flag = 1; // Flag setzen, Übertragung beeendet
53
    }
54
55
    else 
56
  {
57
    UDR = data; // Daten senden
58
    UDR = 0x0A;
59
  }
60
}
61
62
uint16_t ReadChannel(uint8_t mux)
63
{
64
  uint8_t i;
65
  uint16_t result = 0;
66
67
  MyADCSRA = 0; // Interrupt Variable Funktion 
68
  cli(); // Global Interrupt Disabled
69
  
70
  // AD-Wandler wird eingeschaltet
71
   // Frequenzvorteiler Teilungsfaktor 32
72
  // bei 4Mhz Taktfrequenz der CPU 
73
  //
74
  //  AD_TAKT = CPU_TAKT / TEILUNGSFAKTOR = 4Mhz / 32 = |125kHz|
75
  //  PERIODENDAUER = 1 / 125kHz = |8 mikroSec.|
76
  //
77
  // Es werden alle 8 mikroSec. neue werte vom AD-Wandler eingelesen.
78
  // Zusätzlich noch den ADC Interrupt (ADIE) einschalten zur
79
  // weiterverarbeitung für Sleep mode (ADC Noise Canceler)
80
  ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0) | (1 << ADIE);  
81
  
82
  set_sleep_mode(SLEEP_MODE_ADC); // ADC Sleep mode vorwählen
83
  //sleep_mode();
84
  sei(); // Global Interrupt Enabled
85
86
  ADMUX = mux; // Kanal waehlen
87
88
    // Nach Aktivieren des AD-Wandlers wird ein "Dummy-Readout" empfohlen, 
89
  // man liest also einen Wert und verwirft diesen, 
90
  // um den AD-Wandler "warmlaufen zu lassen". 
91
    ADCSRA |= (1 << ADSC);              
92
    while ( ADCSRA & (1 << ADSC)) 
93
  {
94
  
95
  }
96
  
97
  // 10Bit Ergenisregister ADCL und ADCH einlesen, und als 16Bit großen 
98
  // Integer Wert speichern. Werte aus ADCL und ADCH rechtsbündig 
99
    result |= ADCL + (ADCH << 8); 
100
101
  // Messung - Arith. Mittelwert aus 64 aufeinanderfolgenden Wandlungen 
102
    result = 0; 
103
104
    for(i = 0; i < 64; i++)
105
    {
106
    ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
107
    MyADCSRA &= ~(1 << ADIF); // ADC Interrupt Flag setzten  
108
109
    while(!(MyADCSRA & (1 << ADIF))) // Solange Einlesen bis kein Interrupt Flag von ADC vorhanden
110
    {
111
112
    }
113
          
114
    result += ADCL + (ADCH << 8);  // Nach jeder Wandlung Ergebnisse aufaddieren
115
    }
116
117
  ADCSRA &= ~(1 << ADEN); // Nach Wandlungen AD-Wandler deaktivieren 
118
 
119
    result /= 64; // Summe der Wandlungen durch 64 teilen = arith. Mittelwert
120
   return result; // den arith. Mittelwert als Rückgabewert der Funktion ReadChannel
121
}
122
123
void status_LED(uint8_t color) // Zeigt den Zustand an
124
{
125
  DDRB = 0x03; // Port (B) 1-3 als Ausgang  
126
  PORTB = 0xFF; // Alle Led'S aus
127
128
  switch(color) // Variable "color" abfragen
129
  {
130
    case 1:
131
      // Rote LED leuchtet - Alarmzustand
132
      PORTB = status[0];
133
    break;
134
135
    case 2:
136
      // Grüne LED Leuchtet - Alles Super! 
137
      PORTB = status[1];
138
    break;
139
  }
140
}
141
142
void _error(void)
143
{
144
  //reti(); TODO: Ordentliche Fehlerroutine die Interrupts berücksichtigt
145
  PORTC = 0x10; // Einerstelle einschalten
146
  PORTD = 0x06; // E (ERROR) Ausgeben
147
  status_LED(1); // Status LED auf Rot (Fehler aufgetretet)
148
}
149
150
void PrintNumber(int number)
151
{
152
  if(number < 10) // Einstellig
153
  {
154
    if(number < 0) // Wenn Zahl Negativ
155
    {
156
        number = -number; // Wenn Zahl negativ, durch Multiplizieren mit (-1) positiv machen
157
    }
158
159
    segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
160
    segment[1] = 0xFF;  // Zehnerstelle leer lassen
161
  }
162
163
  if(number > 9) // Zweistellig
164
  {  
165
    
166
    if(number > 99) // Wenn Zahl größer als 99, also dreistellig
167
    {
168
      _error(); // Ist ein Fehler aufgetretet Error-Funktion starten
169
    } 
170
    
171
    else
172
    {
173
      segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
174
        segment[1] = digit[ number / 10 ]; // Zehnerstelle bestimmen
175
    }
176
  }
177
}
178
179
double Angle(uint16_t x, uint16_t y, uint16_t z)
180
{
181
  double tilt; // Winkel in Degree (°)
182
183
  //  x-Value | z-Value | Degree
184
  //   409.6  |  614.4  |    0°
185
  //   614.4  |  409.6  |   90°
186
187
  //tilt = (asin((x - 512.0) / 102.4)) * (180 / M_PI); // Einachsige Winkelbestimmung
188
  tilt = (atan((x - 512.0) / 102.4) / (((z - 512.0) / 102.4))) * (180 / M_PI); // Zweiachsige Winkelbestimmung
189
  
190
  return tilt; // Rückgabewert (Winkel) der Funktion
191
}
192
193
void USART_Init(unsigned int ubrr) // USART initialisieren
194
{
195
  UBRRH = (unsigned char) (ubrr >> 8); // Baudrate in Register schreiben
196
  UBRRL = (unsigned char) ubrr;
197
198
  UCSRB = (1 << TXEN) | (1 << RXCIE); // UART TX einschalten, UART RX Complete Interrupt einschalten
199
  UCSRC = (1 << URSEL) | (3 << UCSZ0); // Frame Format einstellen (Asynchron 8N1)
200
}
201
202
void USART_puts(char *data_) 
203
{
204
    if(uart_tx_flag == 1) 
205
  {
206
      strcpy(uart_tx_buffer, data_); // String Daten in den Sendepuffer kopieren
207
         uart_tx_flag = 0; // Flag für 'Senden ist komplett' löschen,
208
        UCSRB |= (1<<UDRIE); // UDRE Interrupt einschalten, los gehts
209
    }
210
}
211
212
int main(void)
213
{
214
  char CSV[7];
215
  double ADC_angle;
216
217
  uint16_t accelx; // Messergebnisse des Kanals C0 (X) (0-1024)
218
  uint16_t accely; // Messergebnisse des Kanals C1 (Y) (0-1024)
219
  uint16_t accelz; // Messergebnisse des Kanals C2 (Z) (0-1024)
220
221
  // Eigenschaften der Ports C und D definieren
222
  DDRC = 0x18; // Nur die Ports 3-5 (C) auf Ausgänge schalten
223
  DDRD = 0xFD; // Port D Komplett als Ausgang für 7 Segment
224
225
  USART_Init(MYUBRR); // USART initialisieren
226
  cli(); // Global Interrupt Disabled
227
228
  wdt_enable(5); // Watchdog hält Wache im 500ms Zyklus
229
230
    TCCR0 |= (1 << CS00); // Vorteiler 1 (Takt 4Mhz)
231
    TIMSK |= (1 << TOIE0); // Timer 0 Overflow Interrupt
232
233
  sei(); // Globale Interrupts einschalten
234
235
  while(1)
236
  {
237
    wdt_reset(); // Watchdog Timer zurücksetzen
238
    status_LED(2); // Status LED auf Grün (Alles OK)
239
    
240
    accelx = ReadChannel(0); // 10Bit - Messwert der x-Achse 
241
    accely = ReadChannel(1); // 10Bit - Messwert der y-Achse
242
    accelz = ReadChannel(2); // 10Bit - Messwert der z-Achse
243
244
    ADC_angle = Angle(accelx, accely, accelz); // Winkel in Variable verstauen
245
  
246
    PrintNumber(ADC_angle); // Wert ausgeben, ganzzahlig nicht negativ
247
    USART_puts(itoa(ADC_angle, CSV, 10)); // Daten auch seriell rausschicken
248
  }
249
250
  return 0; // Wird niemals erreicht
251
}

von Johannes M. (johnny-m)


Lesenswert?

> result |= ADCL + (ADCH << 8);
> result += ADCL + (ADCH << 8);
Liest Du auch, was ich schreibe? Genau das ist Murks! Und Du hast grad 
noch behauptet, dass Du es jetzt in der richtigen Reihenfolge machst. 
Lies noch mal mein Posting von 15:03 und denk drüber nach! Außerdem 
gehört Code von dem Umfang in den Anhang.

von lkmiller (Gast)


Lesenswert?

Und was steht da:
[/c]
uint8_t MyADCSRA;
[c]
Wo ist das jetzt volatile?

von Crashdemon (Gast)


Lesenswert?

Ok, mein Fehler, allerdings hat das nichts an dem Hauptproblem geändert 
wenn ich jetzt die zeilen
1
result |= ADCL + (ADCH << 8);

in
1
result = ADCL;
2
result += (ADCH << 8);

umänder funktioniert der Code auch nicht mehr wenn ich die Optimierung 
austelle.
Ok das volatile habe ich drangehängt allerdings bringt, das keine 
veränderung, ist aber ja an dieser stelle richtig da ich MyADCSRA ja 
global in der Interupt Routine und in den Funktionen nutze.
Vllt. ist da ein grundlegender Fehler mit den Interupt Routinen bin mir 
nicht so ganz sicher wo ein cli() und wo ein sei() hinkommen muss.

von Stefan E. (sternst)


Lesenswert?

1
ISR(USART_UDRE_vect) 
2
...
3
    UDR = data; // Daten senden
4
    UDR = 0x0A;
Da würde ich auch noch mal drüber nachdenken. ;-)

von Johannes M. (johnny-m)


Lesenswert?

Crashdemon wrote:
> Ok, mein Fehler, allerdings hat das nichts an dem Hauptproblem geändert
> wenn ich jetzt die zeilen
>
>
1
> result |= ADCL + (ADCH << 8);
2
>
>
> in
>
>
1
> result = ADCL;
2
> result += (ADCH << 8);
3
>
>
> umänder funktioniert der Code auch nicht mehr wenn ich die Optimierung
> austelle.
Himmelarsch, ist das denn so schwer zu begreifen?
1
result += ADCW;
Und sonst nichts!

> Vllt. ist da ein grundlegender Fehler mit den Interupt Routinen bin mir
> nicht so ganz sicher wo ein cli() und wo ein sei() hinkommen muss.
Nirgends. Das ist schon richtig so.

Dringende Literaturempfehlung:
AVR-GCC-Tutorial

von Michael Wilhelm (Gast)


Lesenswert?

>Himmelarsch, ist das denn so schwer zu begreifen?
>result += ADCW;
>Und sonst nichts!

Doch. result läuft irgendwann über. Dieses += kommt doch noch vom 
getrennten Auslesen.

result = ADCW;

MW

von Johannes M. (johnny-m)


Lesenswert?

Michael Wilhelm wrote:
>>Himmelarsch, ist das denn so schwer zu begreifen?
>>result += ADCW;
>>Und sonst nichts!
>
> Doch. result läuft irgendwann über. Dieses += kommt doch noch vom
> getrennten Auslesen.
>
> result = ADCW;
An einer Stelle braucht er es aber für die Mittelwertbildung mit dem 
"+=". Und das "|=" bei dem ersten Einlesen ist sowieso Käse, weil das 
nur ein Dummy-Read ist und result sofort danach eh wieder gelöscht 
wird. Da kann tatsächlich ein einfaches "=" hin.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>...problem besteht aber weiterhin...
>...nichts am Hauptproblem geändert...

Nur mal angenommen, wir wissen gar nicht so richtig, was das Problem 
ist....

Dann können wir lang an irgendwelchen AD-Registern und volatile 
Deklarationen rumdiskutieren. Oder habe ich was überlesen?

von Johannes M. (johnny-m)


Lesenswert?

Übrigens, nach wie vor stehen im ADMUX noch ein paar Steuerbits 
zusätzlich zu den Multiplexer-Bits, die jedes Mal beim Kanal-Wählen zu 
Null gesetzt werden. Hast Du tatsächlich ne externe Referenz?

Außerdem, was soll eigentlich der Quatsch da:
1
> MyADCSRA &= ~(1 << ADIF); // ADC Interrupt Flag setzten  
2
>
3
> while(!(MyADCSRA & (1 << ADIF))) // Solange Einlesen bis kein Interrupt Flag von ADC vorhanden
?

Umständlicher geht es wohl kaum. Wenn Du schon in einer Schleife 
einliest, dann schalt den Interrupt komplett aus und frag entweder das 
ADSC oder das ADIF direkt ab. Beim Dummy-Read ist es doch genau so 
gemacht, warum also dieser Umstand?

von Crashdemon (Gast)


Lesenswert?

So lese habe das jetzt über result = ADCW gemacht, ich dachte das das 
nicht funzt deswegen hatte ich es in der vergangenheit immer einzeln 
über die ADCL und ADCH gemacht. Nachdem ich das geändert habe funzt es 
auch mit der Optimierung also danke am Johannes für seine Geduld.

Jetzt noch was zu den Fragen, ja ich benutze eine externe Referenzspg.,
zu MyADCSRA, habe auch schon versucht das durch das normale ADCSRA zu 
ersetzen mit dem ergebnis das das Programm dann gar nicht mehr lief.

von Johannes M. (johnny-m)


Lesenswert?

Crashdemon wrote:
> Jetzt noch was zu den Fragen, ja ich benutze eine externe Referenzspg.,
> zu MyADCSRA, habe auch schon versucht das durch das normale ADCSRA zu
> ersetzen mit dem ergebnis das das Programm dann gar nicht mehr lief.
Du kannst entweder das Flag per Software abfragen oder das ganze per 
Interrupt machen, aber nicht beides gleichzeitig! Wenn der Interrupt 
Handler aufgerufen wird, dann wird das Flag automatisch gelöscht. Das 
Hauptprogramm kann dann lange warten. Warum machst Du es nicht einfach 
so, wie Du es bei der ersten Wandlung doch schon korrekt geschrieben 
hast, indem Du ADSC abfragst?

von Crashdemon (Gast)


Lesenswert?

Hmm, habe jetzt beschriebene stelle so umgeändert wie du es gesagt hast:
1
for(i = 0; i < 64; i++)
2
{
3
    ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
4
    
5
    while ( ADCSRA & (1 << ADSC)) 
6
    {
7
8
    }
9
    result += ADCW; // Nach jeder Wandlung Ergebnisse aufaddieren
10
}

das funktioniert auch super, allerdings bin ich jetzt ein wenig confused 
wegen meine ISR, die ich wie folgt geändert habe:
1
ISR(ADC_vect)
2
{
3
    ADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
4
}

muss die ISR überhaupt noch vorhanden sein, da diese ja aufgerufen wird 
wenn ADIF gesetzt ist, was ja jetzt nicht mehr der fall sein kann, habe 
glaube ich ein kleines verständnis Problem, wenn ich sie allerdings ganz 
weglasse funzt es ja auch nicht?

von Johannes M. (johnny-m)


Lesenswert?

Crashdemon wrote:
> Hmm, habe jetzt beschriebene stelle so umgeändert wie du es gesagt hast:
>
>
1
> for(i = 0; i < 64; i++)
2
> {
3
>     ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
4
> 
5
>     while ( ADCSRA & (1 << ADSC))
6
>     {
7
> 
8
>     }
9
>     result += ADCW; // Nach jeder Wandlung Ergebnisse aufaddieren
10
> }
11
>
So weit, so gut...

>
> das funktioniert auch super, allerdings bin ich jetzt ein wenig confused
> wegen meine ISR, die ich wie folgt geändert habe:
>
>
1
> ISR(ADC_vect)
2
> {
3
>     ADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
4
> }
5
>
>
> muss die ISR überhaupt noch vorhanden sein, da diese ja aufgerufen wird
> wenn ADIF gesetzt ist, was ja jetzt nicht mehr der fall sein kann,
Wieso sollte das nicht mehr der Fall sein? Das Flag wird immer 
gesetzt, wenn eine Wandlung beendet wird, egal, ob der Interrupt 
freigegeben ist oder nicht. Außerdem kann man ein Interrupt Flag nicht 
setzen, sondern nur löschen. Wenn Du das ADSC abfragst, was in Deinem 
Fall sinnvoll ist, dann deaktiviere den Interrupt, schmeiß den 
Interrupt Handler (ISR) weg und auch das ganze Trara mit dem myADCSRA.

> habe glaube ich ein kleines verständnis Problem, wenn ich sie
> allerdings ganz weglasse funzt es ja auch nicht?
Ein Interrupt Flag wird immer gesetzt, wenn das entsprechende 
Hardware-Ereignis auftritt, ganz unabhängig davon, ob der betreffende 
Interrupt freigegeben ist oder nicht. Dadurch ist es eben auch möglich, 
Hardware-Ereignisse ohne Interrupt abzufragen (Polling). Allerdings 
muss man, wenn man ein solches Flag abfragt, dieses nach der Abfrage 
auch löschen, weil das dann nicht automatisch durch die Hardware gemacht 
wird. Und Interrupt Flags werden beim AVR nicht durch Hineinschreiben 
einer 0 gelöscht, sondern mit einer 1. Das, was Du oben in Deiner ISR 
geschrieben hast, würde deshalb lediglich das Flag löschen (wenn es 
nicht vorher schon durch die Hardware gelöscht worden wäre).

Beim ADC macht Polling über das Interrupt Flag aber nicht so viel Sinn, 
weil es da einfacher über das ADSC-Bit geht. Das wird nämlich generell 
automatisch gelöscht, wenn die Wandlung abgeschlossen ist und muss 
nicht, wie das Flag, von Hand gelöscht werden.

Wenn man einen Interrupt freigibt, bedeutet das nur, dass die 
Bearbeitung des dazugehörenden Hardware-Ereignisses per Interrupt 
durchgeführt wird (m.a.W., dass das Auftreten des Hardware-Ereignisses 
zu einer Unterbrechung des normalen Programmablaufs führt). Und immer 
drauf achten, dass eine Interrupt-Freigabe aus zwei Stufen besteht: 
lokale Freigabe (über das jeweilige Enable-Bit, im Falle des ADC ist 
das ADIE) und globale Freigabe über das I-Bit im Statusregister SREG 
(in WINAVR-C über die Funktion "sei()"). Wenn ein Interrupt freigegeben 
ist und es keine ISR dazu gibt, führt das zum Reset ("Absturz") des µC, 
wenn das entsprechende Hardware-Ereignis auftritt.

Zu einem freigegebenen Interrupt gehört immer eine ISR, wenn man das 
Flag per Polling abfragen will, muss man den Interrupt über das lokale 
Enable-Bit deaktivieren, sonst knallt's.

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.