Wegoptimiert?

OP #2255945
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?
OP #2255963
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.
OP #2255988
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
}
Gast #2255995
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...
Gast #2256003
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!
OP #2256094
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.
#2256373
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.
OP #2256733
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?
#2256748
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
 }
OP #2256798
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren