Forum: Mikrocontroller und Digitale Elektronik ATmega88 ADC flag ist 0 in Schleife und 1 in if-Anweisung


von MazzMan (Gast)


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
}

von spess53 (Gast)


Lesenswert?

Hi

>  if((ADCSRA & (1 << ADIF)) == (1 << ADIF)){

Dir ist aber klar, das ohne ADC-Interrupt das ADIF-Flag manuell 
zurückgesetzt werden muss?

MfG Spess

von MazzMan (Gast)


Lesenswert?

spess53 schrieb:
> Dir ist aber klar, das ohne ADC-Interrupt das ADIF-Flag manuell
> zurückgesetzt werden muss?

Wenn ich es nicht zurücksetze, dürfe ich aber nicht in einer 
Endlosschleife landen, oder?

von S. Landolt (Gast)


Lesenswert?

> StartADCConversation
Klingt ja sehr unterhaltend, aber wo wird es aufgerufen?

von spess53 (Gast)


Lesenswert?

Hi

>Wenn ich es nicht zurücksetze, dürfe ich aber nicht in einer
>Endlosschleife landen, oder?

Wenn es einmal gesetzt ist, schon.

MfG Spess

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Ob das Alles etwas hilft, solange ADSC nicht gesetzt wird?

von Karl H. (kbuchegg)


Lesenswert?

1
int main(void)
2
{
3
  SP = RAMEND;
4
...

davon lässt du die Finger!
Diese Controller-Register gehen dich allesamt nichts an. Das erledigt 
das C-System schon alles für dich. Wenn du dich da einmischt, dann 
gewinnst du nichts und schiesst dir maximal selber ins Knie.

von Karl H. (kbuchegg)


Lesenswert?

S. Landolt schrieb:
> Ob das Alles etwas hilft, solange ADSC nicht gesetzt wird?

Sollte nicht notwendig sein, wenn der Timer den ADC triggert.
Diese Einstellung hab ich aber noch nicht kontrolliert.

von MazzMan (Gast)


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.

von Karl H. (kbuchegg)


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.

von Thomas E. (thomase)


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.

von S. Landolt (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


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.

von Christian K. (the_kirsch)


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

von Matthias S. (Firma: matzetronics) (mschoeldgen)


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.

: Bearbeitet durch User
von S. Landolt (Gast)


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?

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


Lesenswert?

S. Landolt schrieb:
> weiß der C-Compiler, in welcher Umgebung er sich befindet und macht aus
> dem return automatisch ein reti?
Ja, du selbst gibst ihm mit dem Schlüsselwort ISR() den nötigen Tipp...

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.