Forum: Mikrocontroller und Digitale Elektronik ADC; Messwerte in Konstanten Zeitabständen?


von Klotz (Gast)


Lesenswert?

Hallo!

Ich habe ein kleines Problem mit dem ADC, und zwar möchte ich n 
Messwerte in konstanten Zeitabständen aufnehmen.


Zunächst hatte ich den ADC im Free-Running Mode und dann im 
Timer-Interrupt immer den Wert aus ADCW ausgelesen und in ein Feld der 
Größe n geschrieben.


Dann habe ich gelesen das man das auch über den Interrupt des ADC machen 
kann was evtl. besser wäre (warum stand nicht dabei...) und wollte das 
mal ausprobieren, irgendwie komme ich dabei nicht richtig weiter. Es ist 
doch richtig das ich weiterhin mit dem Timer einen "Takt" erzeuge der 
dann den ADC   startet oder?

Ich verwende einen Mega168, zusammen mit AVR Studio, die Messwerte 
sollen in einem Feld abgespeichert werden.


Gibt es dazu eine genauere Erklärung oder bessere Lösungen? Im Forum 
habe ich einige Sachen in Assembler gefunden, da komme ich leider 
absolut nicht mit klar :(

1
main(void) 
2
{
3
  sei();
4
  
5
  ADMUX = 0;
6
7
  ADMUX |= (0<<REFS1) | (1<<REFS0);
8
  
9
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)| (1<<ADATE) | (1<<ADIE);
10
11
  ADCSRB = (1<<ADTS1) | (1<<ADTS0);
12
13
  ADCSRA |= (1<<ADSC);
14
15
  do
16
  {
17
  messwerte_aufnehmen();
18
  ausgabe();
19
  }
20
  
21
  while(1);
22
}
23
24
25
void messwerte_aufnehmen()
26
{
27
  index=0;
28
  TCCR1A = (1<<COM1A1) | (1<<COM1A0);
29
  cli();
30
  TCNT1H = 0xF0;
31
  TCNT1L = 0x05;
32
  sei(); 
33
  TIMSK1 = (1<<TOIE1);
34
  TCCR1B = (1<<CS10);
35
  while(index<=n-1);
36
  TCCR1B &=~ (0<<CS10);
37
}
38
39
40
SIGNAL (SIG_OVERFLOW1)    
41
{
42
  messwerte[index]  =ADCW;    // ergebnis übernehmen
43
  index++;
44
}

von Thomas (kosmos)


Lesenswert?

du schreibst einfach in die Entsprechende Interrupt-Routine den Start 
der Wandlung rein.

Beim Timerüberlauf oder beim Compare Match wird ja in die entsprechende 
Interrupt-Routine verzweigt und dort setzt du das ADSC-Bit um die 
Wandlung zu starten und verlässt die Interruptroutine wieder und holst 
nach einer gewissen Zeit deinen AD-Wert ab, kannst auch danach polen ob 
die Wandlung fertig ist oder holst ihn erst mittels dem 
"ADC-Fertig-Interrupt" ab dann kannst du zwischenzeitlich andere 
Aufgaben erledigen.

von Werner B. (Gast)


Lesenswert?

Die ATMega8535, Mega16 und Mega32 kennen einen "Trigger ADC on Compare 
Match" Modus.

z.B.
1
SFIOR |= _BV(ADTS0)|_BV(ADTS2);  /* ADC Trigger Source = Timer1 Compare B */
2
ADCSRA = _BV(ADPS2) | _BV(ADPS1) | // prescale faktor= 128 ADC läuft
3
         _BV(ADPS0) | // mit 14,7456MHz/ 128 = 115,2kHz 18,432MHz -> 144kHz
4
         _BV(ADEN)  | // ADC an
5
         _BV(ADSC)  | // Beginne mit der Konvertierung
6
         _BV(ADIE)  | // ADC-Interrupt an
7
         _BV(ADATE);  // Auto trigger on
Der Compare Match Interrupt muss enabled sein, aber in der ISR muss 
nichts (außer return) drinstehen.

Evtl. kann der Mega168 so etwas auch.

von Johannes M. (johnny-m)


Lesenswert?

Werner B. wrote:
> Der Compare Match Interrupt muss enabled sein, aber in der ISR muss
> nichts (außer return) drinstehen.
Nö, der Interrupt muss dafür NICHT freigegeben sein. Nur das 
betreffende Flag muss dann von Hand gelöscht werden.

> Evtl. kann der Mega168 so etwas auch.
Kann er. Das ganze heißt ADC Auto Trigger und geht mit den meisten 
Timer-Overflow- und Timer-Compare-Ereignissen.

Einfach einen Timer auf die gewünschte Zeit einstellen, die ADTS-Bits im 
ADCSRB entsprechend setzen und den Rest macht die Hardware. Nur, wie 
gesagt, falls der Timer-Interrupt nicht freigegeben ist, dran denken, in 
der ADC-ISR das Timer-Overflow- bzw. Compare-Flag zu löschen. Sonst 
geht's nur einmal.

von Klotz (Gast)


Lesenswert?

Also der Teil meines Programmes sieht zur Zeit so aus:

1
  ADMUX = 0;
2
  ADMUX |= (0<<REFS1) | (1<<REFS0);
3
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)| (1<<ADATE) | (1<<ADIE);
4
  ADCSRB = (1<<ADTS1) | (1<<ADTS0);
5
6
void messwerte_aufnehmen()
7
{
8
  index=0;
9
  TCCR1A = (1<<COM1A1) | (1<<COM1A0);
10
  cli();
11
  TCNT1H = 0xF0;
12
  TCNT1L = 0x05;
13
  sei();
14
  TIMSK1 = (1<<TOIE1);
15
  TCCR1B = (1<<CS10);
16
17
  while(index<=n-1);
18
19
  TCCR1B &=~ (0<<CS10);
20
}
21
22
23
SIGNAL (SIG_OVERFLOW1)
24
{
25
  ADCSRA |= (1<<ADSC);
26
}
27
28
SIGNAL(SIG_ADC)
29
{
30
  array_real[index]=ADCW;
31
  index++;
32
}


Ich hatte mir das so vorgestellt das jedesmal wenn die Funktion 
aufgerufen wird das Feld mit den n Messwerten gefüllt wird.

Wäre nett wenn mal jemand mit'm Zaun winken könnte ;)

von Johannes M. (johnny-m)


Lesenswert?

Klotz wrote:
> Ich hatte mir das so vorgestellt das jedesmal wenn die Funktion
> aufgerufen wird das Feld mit den n Messwerten gefüllt wird.
>
> Wäre nett wenn mal jemand mit'm Zaun winken könnte ;)
Winke Winke... War das oben nicht genug mit dem Zaun gewunken? Wenn das 
Programm ansonsten nix zu tun hat, kann man das sicher so ähnlich 
machen. ABER:
1.) Wie oben angedeutet, gibt es die Auto Trigger-Funktion, die Dir 
zumindest einen Interrupt Handler erspart.
2.) Achte darauf, dass die Variable "index" volatile deklariert ist. 
Sonst funktioniert es möglicherweise gar nicht.
3.) Verwende besser nicht das veraltete SIGNAL-Geschisse. Wenn Du noch 
nicht geupdated hast, dann hol es nach und verwende ISR für die 
Interrupt Handler.

EDIT:
Sehe grad, dass Du ja bereits den Auto Trigger aktiviert hast. Warum 
setzt Du dann das ADSC-Bit noch in der Timer-ISR? Das ist Unsinn. Lass 
die ISR ganz weg, deaktiviere den Interrupt und lösche lediglich in der 
ADC-ISR das Timer Overflow Flag mit
1
TIFR1 = 1 << TOV1;

von Klotz (Gast)


Lesenswert?

Vielen Dank für die Hilfe!

Manchmal fällt der Groschen bei mir leider Pfennigweise.

Hab die 3 Punkte nochmal geändert und es läuft jetzt schonmal....bis zum 
nächsten Problem (das dauert hoffentlich noch etwas) ;)

Gruß

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.