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?
könnte es evtl sein, dass der compiler eine leere schleife "wegoptimiert" hat? meine gelesen zu haben, dass sowas vorkommen kann.
Es liegt zu 99.9% an dem Code. Es war bisher eben nur Zufall, dass es funktioniert hat.
...oder man hat einen GCC 4.3.0-2 (debian), der optimiert schon mal uint32_t Operationen (z.B. division) weg...
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?
MyADCSRA gibts in dem Tutorial nicht!! Abhilfe zu deinem MyADCSRA: volatile char MyADCSRA;
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 | }
|
Gibt es irgendeinen Grund für das MyADCSRA anstelle von dem normalen ADCSRA ? PS: ADCL + ADCH kennt der Compiler als ADC
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?
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.
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.
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.
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.
Ja, die Ergebnissreister werden natürlich in der richtigen Reihenfolge ausgelesen:
1 | uint16_t x |
2 | |
3 | x = ADCL; |
4 | x += (ADCH << 8); |
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.
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 | }
|
> 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.
Und was steht da: [/c] uint8_t MyADCSRA; [c] Wo ist das jetzt volatile?
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.
1 | ISR(USART_UDRE_vect) |
2 | ...
|
3 | UDR = data; // Daten senden |
4 | UDR = 0x0A; |
Da würde ich auch noch mal drüber nachdenken. ;-)
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
>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
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.
>...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?
Ü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?
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.
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?
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?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.