Hallo,
ich hätte bezüglich Timer / ISR und ADC eine Frage. Zu erst einmal meine
Konfiguration. Habe ein ATmega88 der den Analog Wert eines Accelerometer
auswerten soll, für den UART ist ein 14,7456 Quarz angeschlossen. Mein
Timer läuft mit einer Frequenz von 1001Hz (geht soweit auch ganz gut
nach 5 min laufen zeigen auch die Variablen die eine Uhr „anzeigen“ 5
min), der ADC mit 115kHz, dieser und der UART funktionieren ebenfalls.
Ich will nun alle ISR (ca. jede 1ms) eine ADC Wandlung starten, 10 Werte
zusammenzählen und den Durchschnitt daraus bilden, sollte eigentlich
schnell genug sein.
Zum Code: Es ist alles noch etwas chaotisch.
1
voidadcInit(uint8_tmux){
2
uint16_tresult;
3
// ADMUX &= ~(1<<REFS1) | (1<<REFS0);
4
ADMUX=mux;
5
// Choose ADC channel and the ref
6
ADMUX=ADC_VREF_TYPE|(1<<ADLAR);
7
// Start ADC and choose of the prescaler
8
ADCSRA=ADC_PRESCALER|(1<<ADEN);
9
// Dummy read out
10
result=adcRead(0);
11
// Delete ADC Interrupt Flag
12
//ADCSRA |= (1<<ADIF);
13
}
14
15
voidadcStart(void){
16
// Choose ADC channel and the ref
17
ADMUX=ADC_CHANNEL_0|ADC_VREF_TYPE;
18
// Start ADC
19
ADCSRA|=(1<<ADSC);
20
}
21
22
uint16_tadcGetValue(void){
23
// Wait for conversation
24
while(ADCSRA&(1<<ADSC));
25
returnADCW;
26
}
27
28
voidtimer0Init(void){
29
//Waveform Generation Mode
30
TCCR0A|=(1<<WGM01);// ctc mode
31
// Set Presaclor
32
TCCR0B|=TIMER_PRESCALER;
33
// Set Timer
34
TCNT0=0;
35
OCR0A=DEF_OCR0A;
36
// Set Compare Interrupt
37
TIMSK0|=COMPARE_BIT;
38
// Activate global interrupts
39
sei();
40
}
41
42
ISR(TIMER0_COMPA_vect){
43
adcStart();
44
globalCount++;
45
}
46
47
intmain(void){
48
uint16_tadcCounter=0,adcSu=0;
49
50
uartInit(MY_UBRR);
51
adcInit(0);
52
timer0Init();
53
adcZeroRate=adcZeroUpdate();
54
55
for(;;){
56
adcSu+=adcGetValue();
57
adcCounter++;
58
if(adcCounter>=9){
59
//printf(" AdcCounter %d\n", adcCounter);
60
printf(" GlobalCount %d\n",globalCount);
61
adcSu=adcSu/(adcCounter+1);
62
63
adcCounter=0;
64
globalCount=0;
65
adcSu=0;
66
}
67
}
68
return0;
69
}
So jetzt zum Problem. Die adcSu ist soweit Korrekt, wenn ich nichts
mache ändert sich dieser auch nicht. Durch Bewegung ändert sich der
Wert. Nur beim globalCount wird mir immer eine 0 ausgegeben, dabei
sollte dort doch eigentlich etwas um die 100 stehen. Wenn ich den
adcCounter mit ausgebe kommt im globalCount eine 20 raus. o.O
Sehe ich das falsch, dass wenn ich adcStart() aufrufe und im Anschluss
in der mainloop adcGetValue(), dieser auf die Konvertierung / Ergebnis
wartet und somit die Schleife eigentlich nicht schneller laufen darf.
Und wenn ja, warum wird mir der globalCount falsch ausgegeben? Bin mir
nun leider nicht sicher ob ich wirklich eine Frequenz von ca. 1ms
schaffe für eine ADC Wandlung. Evt hat einer von euch eine Idee oder ein
Link wo ich mich weiter schlau machen kann.
Gruß Walter
walter schrieb:
> Sehe ich das falsch, dass wenn ich adcStart() aufrufe und im Anschluss> in der mainloop adcGetValue(), dieser auf die Konvertierung / Ergebnis> wartet und somit die Schleife eigentlich nicht schneller laufen darf.
Üerleg einfach mal, was wohl passiert, wenn du adcGetValue() aufrufst
und KEINE adc-Operation im Gange war.
> Und wenn ja, warum wird mir der globalCount falsch ausgegeben?
Hast du kontrolliert, ob der Timerinterrupt auslöst?
globalCount als volatile Variable definiert?
> Bin mir> nun leider nicht sicher ob ich wirklich eine Frequenz von ca. 1ms> schaffe für eine ADC Wandlung. Evt hat einer von euch eine Idee oder ein> Link wo ich mich weiter schlau machen kann.
Machs doch anders.
Wenn du sowieso weißt, dass der ADC keine Millisekunde für die Wandlung
benötigt, dann hol dir im Timer Interrupt den Wert vom ADC und starte
eine neue Wandlung. Wenn der nächste Interrupt dann kommt, ist diese
Wandlung bereits fertig, du holst dir das Ergebnis und startest die
nächste ....
Hallo Karl heinz Buchegger,
danke dir für deine Antwort. Ist schön, dass hier immer so schnell
geholfen wird.
zu No.1
eigentlich muss der Code in der while-schleife gefangen sein, bis die
ISR den ADC startet. Und da adcSu = adcSu / (adcCounter+1); den
richtigen Wert bringt. Hoffentlich Irre ich mich nicht. Bin mir aber
leider nicht sicher ob ich wirklich die Zeiten einhalte.
zu No.2
Jup, die ISR wird aufgerufen. Und natürlich sind die Globalen Variablen
mit volatile initialisiert ;)
zu No.3
Ich werde es mal versuchen. Hoffentlich kommt es zu keinen Verzögerungen
:D
Teste es mal aus. Evt versuch ich es auch mit
ISR (ADC_vect)
somit sollte ich sogar eine noch genauere Auflösung hinbekommen.
Danke dir.
Hallo walter,
schau Dir mal im Datenblatt das ADCSRB Register an. Du kannst eine
Wandlung automatisch bei auftreten des TimerInterupt auslösen lassen.
Ich würde die Werte dann im ADC Interrupt summieren und alle 10 Werte
ein Flag setzen, dass der neue Wert zur verfügung steht und abgeholt
werden kann.
Grüße
Timo
walter schrieb:
> Hallo Karl heinz Buchegger,>> danke dir für deine Antwort. Ist schön, dass hier immer so schnell> geholfen wird.>> zu No.1> eigentlich muss der Code in der while-schleife gefangen sein, bis die> ISR den ADC startet.
Warum sollte sie?
Dein Code wartet darauf, dass das ADSC Bit 0 wird bzw. 0 ist.
Was aber, wenn es sowieso schon 0 ist, weil der Timer Interrupt noch gar
nicht da war, der es auf 1 gesetzt hätte?
Dann wird auf nichts gewartet, sondern sofort ADCW ausgelesen und
zurückgekehrt.
Im Vergleich zu der Häufigkeit, mit der diese Funktion aufgerufen wird,
kommt aber der Timer Interrupt nur alle heiligen Zeiten. Dadurch ergibt
sich diese Sequenz
Timer-ISR Hauptprogramm
------------------------------------------------
startet
setzt ADSC auf 1
adcGetValue()
wartet auf ADSC == 0
ADC wird fertig
ADSC <- 0
return ADCW
adcGetValue()
ADSC ist bereits 0
return ADCW
adcGetValue()
ADSC ist bereits 0
return ADCW
adcGetValue()
ADSC ist bereits 0
return ADCW
startet
setzt ADSC auf 1
adcGetValue()
wartet auf ADSC == 0
ADC wird fertig
ADSC <- 0
return ADCW
adcGetValue()
ADSC ist bereits 0
return ADCW
Die meiste Zeit findet adcGetValue das ADSC Bit als nicht gesetzt vor,
weil die Timer ISR den ADC noch gar nicht gestartet hat, seit
adcGetValue zuletzt den Wert abgeholt hat. Des kümmert adcGetValue aber
nicht, es liefert dann einfach den zuletzt gewandelten Wert noch einmal.
Das Problem ist, dass adcGetValue eben nicht auf das Beenden einer
Wandlung wartet, sondern nur darauf, dass das bewusste Bit 0 ist.
Oder anders ausgedrückt: Wenn du 20 mal am Tag in deinen Postkasten
schaust und ihn leer vorfindest, heisst das nicht, dass der Postler 20
mal da war und nichts hineingelegt hat.