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
voidInitADC(void);
7
voidInitPorts(void);
8
voidInitPWM(void);
9
voidInitTimer(void);
10
voidCommutate(void);
11
voidStartADCConversation(void);
12
13
14
charphase;
15
uint16_ttime;
16
uint16_tdelay;
17
boolblind_start;
18
19
charsteps[]={
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
chartest=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
intmain(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
voidInitPorts(){
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
voidInitTimer(){
99
//ATMEGA88
100
TCCR0B=0b00000001;//no prescaler
101
TIMSK0=(1<<TOIE0);
102
sei();
103
}
104
105
voidInitPWM(){
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
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?
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.
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.
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.
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.
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.
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.
Bitte um Nachsicht für einen Assemblerprogrammierer; die erste ISR ist
mit return abgeschlossen, die zweite nicht - macht der Compiler das
Richtige daraus?
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.
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
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.
> 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?
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...