ATmega88 ADC flag ist 0 in Schleife und 1 in if-Anweisung

Gast #4065647
Lesenswert?

Ich habe ein nerviges Problem, an dem ich schon eine weile lang Sitze. 
Ich habe mein ADC auf auto trigger beim Overflow von Timer1 gesetzt und 
den Overflow-Interrupt für Timer 1 aktiviert. Da mein ADC immer dann 
getriggert wird, wenn der Interrupt für Timer1 aufgerufen wird, möchte 
ich dort eine Warteschleife einfügen, die auf mein ADC wartet. Hier ist 
das Problem: Die Warteschleife wird zur Endlosschleife:
1
//PWM overflow interrupt at bottom
2
ISR(TIMER1_OVF_vect){
3
  //wait for the adc to finish
4
  while ((ADCSRA & (1 << ADIF)) == 0){}
5
}

Das bedeutet, dass ADIF anscheinend immer 0 ist. Nun habe ich den Code 
geändert und einen Ausgabe auf PINB3 gemacht, sodass ich dort eine 1 
erhalte, wenn ADIF 1 ist. Sonst erhalte ich eine 0.
1
//PWM overflow interrupt at bottom
2
ISR(TIMER1_OVF_vect){
3
  //wait for the adc to finish
4
  //while ((ADCSRA & (1 << ADIF)) == 0){}
5
  
6
  if((ADCSRA & (1 << ADIF)) == (1 << ADIF)){
7
    PORTB |= (1<<PINB3);
8
  }else{
9
    PORTB &= !(1<<PINB3);
10
  }
11
}

Komischerweise erhalte ich eine 1, was bedeutet, dass ADIF 1 ist. Ich 
habe es auch schon mit for-Schleifen versucht, nun aber keine Ahnung 
mehr was ich noch alles machen soll. Wäre nett, wenn mir einer Helfen 
könnte. Hier der komplette Code (nach dem ihr immer fragt):
1
#include <asf.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <stdbool.h>
5

6
void InitADC(void);
7
void InitPorts(void);
8
void InitPWM(void);
9
void InitTimer(void);
10
void Commutate(void); 
11
void StartADCConversation(void);
12

13

14
char phase;
15
uint16_t time;
16
uint16_t delay;
17
bool blind_start;
18

19
char steps[] = {
20
  0b10010000,
21
  0b10000100,
22
  0b00100100,
23
  0b01100000,
24
  0b01001000,
25
  0b00011000  
26
};
27

28
ISR(TIMER0_OVF_vect)//every 256µs
29
{
30
  time++;
31
  if(blind_start){
32
    //blind start
33
    if(time >= delay){
34
      time = 0;
35
      
36
      Commutate();
37
      if(delay > 10)
38
        delay -= 1;
39
      //else
40
      //  blind_start = false;
41
    }
42
  }else{
43
  
44
  }
45
  return;
46
}
47

48
char test = 1;
49

50
//PWM overflow interrupt at bottom
51
ISR(TIMER1_OVF_vect){
52
  //wait for the adc to finish
53
  //while ((ADCSRA & (1 << ADIF)) == 0){}
54
  
55
  if((ADCSRA & (1 << ADIF)) == (1 << ADIF)){
56
    PORTB |= (1<<PINB3);
57
    test = 0;
58
  }else{
59
    PORTB &= !(1<<PINB3);
60
    test = 1;
61
  }
62
  //ADCSRA |= (1<<ADIF);
63
}
64

65
int main(void)
66
{
67
  SP = RAMEND;
68
  
69
   InitPorts();
70
   InitTimer();
71
   InitPWM();
72
   InitADC();  
73
  
74
  phase = -1;
75
  time = 0;
76
  delay = 100;
77
  
78
  OCR1B = 200;
79
  
80
  blind_start = true;
81
  
82
  //ADCSRA |= (1<<ADSC);    
83
  
84
    while(1)
85
    {
86
  }
87
}
88

89
void InitPorts(){
90
  //PWM output
91
  DDRB = (1<<DDB3)|(1<<DDB2);
92
  //BLDC output
93
  DDRD = 0xff;
94
  //ADC input
95
  DDRC = (0<<DDC0) | (0<<DDC1) | (0<<DDC3);
96
}
97

98
void InitTimer(){ 
99
  //ATMEGA88
100
  TCCR0B = 0b00000001; //no prescaler
101
  TIMSK0 = (1<<TOIE0);
102
  sei(); 
103
}
104

105
void InitPWM(){
106
  //ATMEGA88
107
  TCCR1A = (1 << COM1B1)|(0 << COM1B0) | (0 << WGM11)|(1 << WGM10); //clear on match when up, set on match when down; phase correct 8-bit
108
  TCCR1B = (0 << WGM13)|(0 << WGM12) | (0 << CS12)|(1<<CS11)|(0<<CS10); //prescaler: 8
109
  TIMSK1 = (1<<TOIE1);//enable timer1 overflow interrupt
110
  
111
}
112

113
void InitADC(){
114
  ADMUX  = (0<<REFS1)|(1<<REFS0) | (1<<ADLAR) | (0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);//reference: AREF; right adjust; channel 2 selected in the beginning
115
  ADCSRA = (1<<ADEN) | (0<<ADSC) | (1<<ADATE) | (1 << ADIF) | (0<<ADIE) | (0<<ADPS2)|(0<<ADPS1)|(1<<ADPS0);//enable adc and auto trigger; prescaler: 8; 
116
  ADCSRB = (1<<ADTS2)|(1<<ADTS1)|(0<<ADTS0);//trigger at timer1 overflow
117
}
118

119
void StartADCConversation(){
120
  ADCSRA |= (1<<ADSC);
121
}
122

123
void Commutate(){
124
  phase++;
125
  if(phase > 5)
126
    phase = 0;
127
  
128
  PORTD = steps[phase];
129
  
130
  //switch adc channel
131
  /*
132
  char channel = (ADMUX & 3)+1;
133
  if(channel < 2)
134
    channel = 0;
135
  
136
  ADMUX = (ADMUX & 0b11111000) | channel;*/
137
}
#4065751
Lesenswert?

wenn du schon den ADC vom Timer aus triggern lässt, warum lässt du dir 
dann nicht ganz einfach auch vom ADC aus einen Interrupt triggern, wenn 
er fertig ist.
Der ADC kann das, du musst das nur aktivieren und eine entsprechende ISR 
dafür schreiben. Die wird dann aufgerufen, wenn der ADC ein neues 
Ergebnis fertig hat.
#4065760
Lesenswert?

Tu dir selbst einen Gefallen und ...
1
void InitTimer(){ 
2
  //ATMEGA88
3
  TCCR0B = 0b00000001; //no prescaler
4
  TIMSK0 = (1<<TOIE0);
5
  sei(); 
6
}
verfrachte den sei() hier hin
1
int main(void)
2
{
3
....
4

5
  sei();
6
  
7
  while(1)
8
  {
9
  }
10
}
also unmittelbar vor die Hauptschleife. Denn das ist die einzige Stelle 
an der du mit Sicherheit sagen kannst, das alles korrekt und fertig 
eingerichtet ist und daher die Interrupts loslegen können.

Dieses Verpacken des sei() in irgendwelche Funktionen ist IMHO keine 
gute Idee. Alles vor dem sei() ist Setup bzw. Initialisierung von 
irgendwelchen Komponenten. D.h während diese Programmteile ablaufen, 
kannst du nicht 100% sicher davon ausgehen, dass zu diesem Zeitpunkt 
jeweils das Gesamtsytem schon lauffähig wäre.
Gast #4065778
Lesenswert?

S. Landolt schrieb:
>> StartADCConversation
> Klingt ja sehr unterhaltend, aber wo wird es aufgerufen?

Der ADC wird automatisch getriggert, somit muss ich es nicht noch manuel 
manchen. Die Funktion wird nirgendwo aufgerufen.

spess53 schrieb:
> Wenn es einmal gesetzt ist, schon.

Hmmm das versteh ich jetzt aber nicht. In der Schleife wird doch 
überprüft ob ADIF gleich null ist. Wenn es aber einmal auf eins ist und 
ich es nicht zurücksetze, dann bleibt es ja bei eins und die Bedingung 
ist nicht mehr erfüllt, weil ADIF ja ungleich null ist. Somit müsste er 
aus der schleife raus springen.
Ich habe ea aber mal versucht und am ende des interrupts
1
ADCSRA |= (1<<ADIF);
 eingefügt  um ADIF zurück zu setzen. Hat leider auch nicht geholfen.
#4065787
Lesenswert?

So
1
  if((ADCSRA & (1 << ADIF)) == (1 << ADIF)){
2
    PORTB |= (1<<PINB3);
3
  }else{
4
    PORTB &= !(1<<PINB3);
5
  }
wird das nichts.

EIn Bit wird zurückgesetzt mittels
1
    PORTB &= ~(1<<PINB3);

binäre Negation und nicht logische Negation.
Deine LEd an PINB3 zeigt dir nicht die Wahrheit an.
#4065812
Lesenswert?

Karl Heinz schrieb:
> wenn du schon den ADC vom Timer aus triggern lässt, warum lässt du dir
> dann nicht ganz einfach auch vom ADC aus einen Interrupt triggern, wenn
> er fertig ist.
> Der ADC kann das, du musst das nur aktivieren und eine entsprechende ISR
> dafür schreiben. Die wird dann aufgerufen, wenn der ADC ein neues
> Ergebnis fertig hat.

Das ist gar nicht nötig. Wenn der ADC vom Timer getriggert wird und die 
Timings vernünftig abgestimmt sind, also die Wandlungszeit kürzer als 
die Intervalzeit des Timers ist, alles andere macht auch keinen Sinn, 
braucht man die ADC-Flags gar nicht abfragen, sondern lediglich in der 
Timer-ISR das ADC-Register auslesen. Denn der ADC ist zu diesem 
Zeitpunkt definitiv fertig.
Der ADC-Wert im ersten Aufruf der Timer-ISR ist logischerweise 
unbrauchbar. Aber das sollte einen grossen Geist nicht stören.

mfg.
#4066072
Lesenswert?

S. Landolt schrieb:
> Bitte um Nachsicht für einen Assemblerprogrammierer; die erste ISR ist
> mit return abgeschlossen, die zweite nicht - macht der Compiler das
> Richtige daraus?

Ja.

Du denkst zuviel Assembler.
Was soll denn der Compiler am Ende einer Funktion machen, wenn da kein 
return steht? Die Funktion ist zu Ende, wie solls weiter gehen?
Natürlich macht sich der COmpiler da einen return rein, was soll er denn 
auch sonst tun?

Ein explizites return am Funktionsende macht nur dann Sinn, wenn es 
einen Returnwert gibt. Das wiederrum kann aber bei einer ISR gar nicht 
sein.
#4066089
Lesenswert?

Thomas Eckmann schrieb:
> Das ist gar nicht nötig. Wenn der ADC vom Timer getriggert wird und die
> Timings vernünftig abgestimmt sind, also die Wandlungszeit kürzer als
> die Intervalzeit des Timers ist, alles andere macht auch keinen Sinn,
> braucht man die ADC-Flags gar nicht abfragen, sondern lediglich in der
> Timer-ISR das ADC-Register auslesen. Denn der ADC ist zu diesem
> Zeitpunkt definitiv fertig.
> Der ADC-Wert im ersten Aufruf der Timer-ISR ist logischerweise
> unbrauchbar. Aber das sollte einen grossen Geist nicht stören.

Man kann den ADC auch als Trigger Quelle direkt mit dem Timer 
verkupfern.
Dann brauch man keine Timer-ISR aus zu programmieren. Der ADC startet 
dann automatisch bei einem Timer Overflow bzw. Compare Match.

ADC-Wert auslesen würd ich dann über die ADC-ISR.


PS.
wie ich gerade sehe hattest du das in deinem ersten Post auch so 
vorgehabt:
ADCSRB = (1<<ADTS2)|(1<<ADTS1)|(0<<ADTS0);//trigger at timer1 overflow
(Firma: matzetronics) #4066099
Lesenswert?

Karl Heinz schrieb:
> Ein explizites return am Funktionsende macht nur dann Sinn, wenn es
> einen Returnwert gibt. Das wiederrum kann aber bei einer ISR gar nicht
> sein.

Man kann allerdings ein return benutzen, um mittendrin aus der ISR 
auszusteigen. So, wie das da steht, kann man es aber ersatzlos 
weglassen.

Auch mir gefällt die Strategie, in der ISR auf den ADC zu warten, 
überhaupt nicht. Was soll denn der Motor machen, wenn kommutiert werden 
soll, der ADC aber noch am Werkeln ist? Unrunder Lauf wäre da noch das 
wenigste. ISR sind nun mal 'Blocking' und das möchte man so wenig wie 
möglich.
Gast #4066125
Lesenswert?

> Du denkst zuviel Assembler.
Jaja, mein Problem, ich 'denke nur Assembler'. Und da gibt es eben ret 
und reti - weiß der C-Compiler, in welcher Umgebung er sich befindet und 
macht aus dem return automatisch ein reti?

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