Forum: Compiler & IDEs Wegoptimiert?


von Christian Q. (osx)


Lesenswert?

Hi! Ich muss in meiner TimerISR ein komisches Verhalten feststellen. 
Vorab der Code:
1
volatile int Timer1;
2
int i = 0;
1
ISR(TIMER1_COMPA_vect){  
2
  
3
  Timer1++;
4
  if(Timer1 == 20) {
5
    Timer1 = 0;
6
    for (i = 0; i < 20; i++) {
7
      printf("%d\n", i);
8
    } 
9
  }   
10
}

Es wird überhaupt nichts getan. Im Simulator kann ich sehen wie die 
Variablen Timer1 und i 'not in scope' sind, dann auf 0, später auf 1 und 
dann weder auf 'not in scope' wechseln.
Lasse ich eine LED beim ISR-Aufruf toggeln, dann ändert sich auch der 
Zustand. Versuche ich dies im if-Block tut sie es jedoch nicht.
Breakpoints lassen sich erst gar nicht setzen.

Das Problem ist bei jedem optimierungslevel in den Projekteinstellngen.
Hat jemand Ideen wie ich vorgehen kann um das Problem zu lokalisieren?

von (prx) A. K. (prx)


Lesenswert?

"i" global ist ungut. Gehört in die ISR.

Debuggen mit eingeschalteter Optimierung sieht nicht immer befriedigend 
aus.

von Εrnst B. (ernst)


Lesenswert?

Christian Q. schrieb:
> Variablen Timer1 und i 'not in scope' sind, dann auf 0, später auf 1 und
> dann weder auf 'not in scope' wechseln.

Soweit doch richtig. du musst warten bis Timer1 auf 20 steht, erst dann 
läuft er in dein if...


Abgesehen davon: printf in der ISR ist 'bäh'. Zum Debuggen aber 
verzeihbar...

von Christian Q. (osx)


Lesenswert?

A. K. schrieb:
> "i" global ist ungut. Gehört in die ISR.
eher so?:
1
ISR(TIMER1_COMPA_vect){  
2
  int i;
3
  Timer1++;
4
  if(Timer1 == 20) {
5
  PORTD ^= (1 << PD6);
6
    Timer1 = 0;
7
    for (i = 0; i < 20; i++) {
8
      printf("%d\n", i);
9
    } 
10
  }   
11
}
Das Problem ist dann aber doch, dass i immer wieder neu initialisiert 
wird, oder nicht?


Er erreicht aber den if-Block nie. mysteriös.. aber  Computer haben 
immer Recht.

von Peter II (Gast)


Lesenswert?

Christian Q. schrieb:
> eher so?:

noch besser so:
1
 for ( uint_8 i = 0; i < 20; i++) {

von Hans (Gast)


Lesenswert?

Zeig doch einmal das komplette Programm!

von Εrnst B. (ernst)


Lesenswert?

1
ISR(TIMER1_COMPA_vect){  
2
  static uint8_t timer1; // braucht kein "volatile"
3
  timer1++;
4
  if(timer1 == 20) {
5
    PIND = (1 << PD6);
6
    timer1 = 0;
7
    for (int8_t i = 0; i < 20; i++) {
8
       printf("%d\n", i);
9
    } 
10
   }   
11
}
:)

von (prx) A. K. (prx)


Lesenswert?

Christian Q. schrieb:

> Das Problem ist dann aber doch, dass i immer wieder neu initialisiert
> wird, oder nicht?

Wird es doch von dir sowieso.

von Ralf (Gast)


Lesenswert?

Wird 'Timer1' nur in der ISR verwendet? (Dann besser 'static') Oder wird 
die vielleicht in der 'main()' heimlich zurückgesetzt?

von Ralf (Gast)


Lesenswert?

Ralf schrieb:
> (Dann besser 'static')
zu langsam ...

von Christian Q. (osx)


Lesenswert?

1
//#include <avr/io.h>
2
#include <stdio.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
#include "UART.h"
7
#include "ADC.h"
8
9
int Timer1 = 0;
10
11
//Array für 20 Messungen
12
int iMessungen[20];
13
14
int main() {
15
  DDRD = 255;
16
  PORTD = 0;
17
  
18
  // 16-Bit Timer
19
  TCCR1B |= (1 << WGM12); //CTC Modus
20
  OCR1A  = 5000;                 
21
  TIMSK = (1<<OCIE1A);  
22
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);  // Prescaler 1
23
24
  //External Interrupt
25
  //MCUCR |= (1<<ISC01) | (0<<ISC00);
26
  //GICR |= (1<< INT0);   
27
  
28
  uart_init();
29
  ADC_Init();
30
  
31
  //Ausgabumleiten
32
  stdout = &mystdout;
33
34
  sei();
35
  while( 1 ) {}
36
}    
37
38
ISR(TIMER1_COMPA_vect){  
39
40
  Timer1++;
41
  if(Timer1 == 20) {
42
  PORTD ^= (1 << PD6);
43
    Timer1 = 0;
44
    for (uint8_t i = 0; i < 20; i++) {
45
      printf("%d\n", i);
46
      PORTD = i;
47
    } 
48
  }   
49
}
50
51
ISR(INT0_vect) {
52
  PORTD ^= (1 << PD6);
53
}

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


Lesenswert?

Εrnst B✶ schrieb:
> Abgesehen davon: printf in der ISR ist 'bäh'. Zum Debuggen aber
> verzeihbar...

Aber nicht in einer Timer-ISR: es könnte gut sein, dass der
nächste Interrupt schon da ist, während die ISR noch dabei ist,
die vorherigen 20 printfs abzuarbeiten (UART bei 9600 Bd: 1 ms
pro Zeichen).

von Ralf (Gast)


Lesenswert?

Kann das sein, das die 'while()'-Schleife wegoptimiert wird? Das könnte 
erklären, dass der Interrupt vorher mal angesprungen wird (während der 
ganzen Initialisierungen). Und dann ist 'main()' zu Ende...

von Christian Q. (osx)


Lesenswert?

Jörg Wunsch schrieb:
> es könnte gut sein, dass der
> nächste Interrupt schon da ist, während die ISR noch dabei ist,
> die vorherigen 20 printfs abzuarbeiten (UART bei 9600 Bd: 1 ms
> pro Zeichen).

Dann würde aber bestimmt mindestens 1 Zeichen ausgegeben oder PD6 
toggled.

Achso: Frequenz ist 1 Mhz.

von (prx) A. K. (prx)


Lesenswert?

Ralf schrieb:

> Kann das sein, das die 'while()'-Schleife wegoptimiert wird?

Nein.

von Ralf (Gast)


Lesenswert?

Christian Q. schrieb:
> Jörg Wunsch schrieb:
>> es könnte gut sein, dass der
>> nächste Interrupt schon da ist, während die ISR noch dabei ist,
>> die vorherigen 20 printfs abzuarbeiten (UART bei 9600 Bd: 1 ms
>> pro Zeichen).
>
> Dann würde aber bestimmt mindestens 1 Zeichen ausgegeben oder PD6
> toggled.
Fängt man aber gar nicht erst an, Das ist dann nämlich die nächste 
Baustelle!

von Peter II (Gast)


Lesenswert?

schau doch einfach mal in den ASM code, diese stelle sollte ja recht 
einfach sein.

von Christian Q. (osx)


Angehängte Dateien:

Lesenswert?

Puh, im ASM finde ich mich noch weniger zurecht.
Ich hab mal die Dateien gepackt und angehangen.

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


Lesenswert?

Schmeiß doch das printf() aus der ISR raus.  Wenn du dort einen
Pin wackeln lässt, dann denk dran, dass der ziemlich schnell wackelt.
Falls du da nur eine LED an PORTD0 dran hast, wirst du das Wackeln
nicht sehen ;-), sondern du brauchst ein Oszilloskop dafür.

von Christian Q. (osx)


Lesenswert?

1
#include <stdio.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
#include "UART.h"
6
#include "ADC.h"
7
8
//int Timer1 = 0;
9
10
//Array für 20 Messungen
11
int iMessungen[20];
12
13
int main() {
14
  DDRD = 255;
15
  PORTD = 0;
16
  
17
  // 16-Bit Timer
18
  TCCR1B |= (1 << WGM12); //CTC Modus
19
  OCR1A  = 50000;                 
20
  TIMSK = (1<<OCIE1A);  
21
  TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);  // Prescaler 1
22
23
  //External Interrupt
24
  //MCUCR |= (1<<ISC01) | (0<<ISC00);
25
  //GICR |= (1<< INT0);   
26
  
27
  uart_init();
28
  //ADC_Init();
29
  
30
  //Ausgabe umleiten
31
  //stdout = &mystdout;
32
33
  sei();
34
  while( 1 ) {}
35
}    
36
37
ISR(TIMER1_COMPA_vect){  
38
  static uint8_t Timer1;
39
  Timer1++;
40
  if(Timer1 == 20) {
41
    Timer1 = 0;
42
    for (uint8_t i = 0; i < 2; i++) {
43
      PORTD ^= (1<<PD6);
44
    } 
45
  }   
46
}
47
48
ISR(INT0_vect) {
49
  PORTD ^= (1 << PD6);
50
}

Mit ADC_init ausgeklammert und OCR1A auf 50000 erhöht blinkt die LED 
ganz kurz.

ADC.h
1
#ifndef _ADC_H_
2
#define _ADC_H_
3
4
#include "ADC.h"
5
6
/* ADC initialisieren */
7
void ADC_Init(void);
8
9
/* ADC Einzelmessung */
10
uint16_t ADC_Read( uint8_t channel );
11
12
/* ADC Mehrfachmessung mit Mittelwertbbildung */
13
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average );
14
15
#endif
ADC.c
1
#include <avr/io.h>
2
#include <stdio.h>
3
#include <avr/interrupt.h>
4
#include <inttypes.h>
5
#include "ADC.h"
6
7
/* ADC initialisieren */
8
void ADC_Init(void) {
9
10
  uint16_t result;
11
12
    ADMUX = (1<<REFS1) | (1<<REFS0);      // interne Referenz: 2.56 Volt 
13
  ADCSRA = ((1<<ADPS2) | 0<<ADPS1) | (0<<ADPS0);     // Frequenzvorteiler
14
  ADCSRA |= (1<<ADIE);    // Interrupt
15
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
16
17
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
18
  also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
19
20
  ADCSRA |= (1<<ADSC);                  // eine ADC-Wandlung 
21
  while (ADCSRA & (1<<ADSC) ) {}        // auf Abschluss der Konvertierung warten
22
  /* ADCW muss einmal gelesen werden, sonst wird Ergebnis der nächsten
23
  Wandlung nicht übernommen. */
24
  result = ADCW;
25
}
26
27
/* ADC Einzelmessung */
28
uint16_t ADC_Read( uint8_t channel ) {
29
  // Kanal waehlen, ohne andere Bits zu beeinflußen
30
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
31
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
32
  while (ADCSRA & (1<<ADSC) ) {}  // auf Abschluss der Konvertierung warten
33
  return ADCW;                    // ADC auslesen und zurückgeben
34
}
35
36
/* ADC Mehrfachmessung mit Mittelwertbbildung */
37
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average ) {
38
  uint32_t result = 0;
39
40
  for (uint8_t i = 0; i < average; ++i )
41
  result += ADC_Read( channel );
42
43
  return (uint16_t)( result / average );
44
}

Komischer Weise geht nach dem Build and Run auch gleich das 
Disassembler-Fenster auf.

von Karl H. (kbuchegg)


Lesenswert?

>  ADCSRA |= (1<<ADIE);    // Interrupt

Man gibt keinen Interrupt frei, für den man keine ISR hat.
Niemals!

von Christian Q. (osx)


Lesenswert?

Oh ja. Danke für den Hinweis, das hab ich übersehen.
Leider ändert es überhaupt nichts am Verhalten.

von Karl H. (kbuchegg)


Lesenswert?

Mach mal diese Variante der ISR
1
ISR(TIMER1_COMPA_vect){  
2
  static uint8_t Timer1;
3
  Timer1++;
4
  if(Timer1 == 20) {
5
    Timer1 = 0;
6
//    for (uint8_t i = 0; i < 2; i++)
7
    {
8
      PORTD ^= (1<<PD6);
9
    } 
10
  }   
11
}

Du musst schon sehr gut sein, wenn du das aufglimmen einer LEd in der 
Dauer von ca 1µs sehen willst. Auf meinem 4Mhz Mega16 kann ich das 
gerade noch erahnen. Aber auch nur weil ich weiß, an welcher LED sich 
was tun müsste. OHne dieses wissen, würde ich das niemals sehen.

Bei
    for (uint8_t i = 0; i < 20; i++)

hingegen, ist das regelmässige Aufblitzen schon deutlich zu sehen.

von Christian Q. (osx)


Lesenswert?

Es muss ein Fehler beim ADC sein. Wenn ich das init auskommentiere 
funktioniert alles wie gewohnt. Naja okay in AVR Studio lassen sich 
immernoch keine Breakpoints setzen und es springt in den Disassembler 
aber auf der Hardware funktioniert alles wie erwartet.
Hab ich den ADC falsch eingebunden?

von Εrnst B. (ernst)


Lesenswert?

Christian Q. schrieb:
> Hab ich den ADC falsch eingebunden?

Hast du:

Karl Heinz Buchegger schrieb:
> Man gibt keinen Interrupt frei, für den man keine ISR hat.
> Niemals!

wirklich beachtet, oder ohne Test einfach:

Christian Q. schrieb:
> Leider ändert es überhaupt nichts am Verhalten.

behauptet?

Schaltest du sonst noch andere Interrupts ein, für die du keine ISR 
hast?
ggfs einen bad_isr handler installieren, der dir das debuggt.
1
ISR(BADISR_vect) 
2
 { 
3
   while (1) {
4
     PORTA ^= 0x42; // Hier: irgendwelche Debug-LEDs blinken lassen 
5
   }
6
 }

von Christian Q. (osx)


Lesenswert?

Ich hatte es getestet. Auf deinen Hinweis jetzt sogar erneut. Und siehe 
da jetzt geht es. Entweder ich habe bei meinem Test den Interrupt aus 
Versehen angelassen oder ein anderes Ergebnis erwartet. Vielen Dank für 
deinen unermüdlich durchgesetzten Lösungsvorschlag :D

Warum sich AVR-Studio trotzdem so komisch verhält verstehe ich nicht.

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.