Forum: Compiler & IDEs A/D-Routine im Atmega128


von Owen S. (senmeis)


Lesenswert?

Servus,

ich verwende alle 8 A/D-Kanäle vom Atmega128. Bisher sieht meine 
A/D-Routine wie folgt aus:
1
ISR(ADC_vect) 
2
{ 
3
   static UINT8 adc_channel;
4
   static UINT16 m[8]; 
5
   
6
   m[adc_channel] = (3 * m[adc_channel] + ADC) >> 2;  // Low Pass Filter 
7
   adc_result[adc_channel] = (m[adc_channel] - 512) * 64;  // convert to INT16
8
   adc_channel++; 
9
   if (adc_channel == 8) 
10
       adc_channel = 0; 
11
   ADMUX = (ADMUX & 0xE0) | adc_channel; 
12
 
13
   ADCSRA |= (1 << ADSC);   // start next conversion 
14
}
D.h. A/D-Wandlung mit A/D-Interrupt direkt nacheinander aufrufen. Es 
gibt einen Nachteil, dass man die Abtastrate nicht feststellen kann. 
Frage, kann man stattdessen einen 1ms-Timer wie folgt einsetzen:
1
void 1ms_TimerInterrupt(void)
2
{
3
  ADCSRA |= (1 << ADSC);   // start next conversion
4
}
5
6
ISR(ADC_vect) 
7
{ 
8
   static UINT8 adc_channel;
9
   static UINT16 m[8]; 
10
   
11
   m[adc_channel] = (3 * m[adc_channel] + ADC) >> 2;  // Low Pass Filter 
12
   adc_result[adc_channel] = (m[adc_channel] - 512) * 64;  // convert to INT16
13
   adc_channel++; 
14
   if (adc_channel == 8) 
15
       adc_channel = 0; 
16
   ADMUX = (ADMUX & 0xE0) | adc_channel;  
17
}
D.h. die Abtastrate ist nun 1000Hz/8 = 125Hz.

Welche Methode ist üblicher?

Gruss
Senmeis

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Die Methode mit Timer ist IMHO eleganter und wenn man einen Timer dafür 
frei hat oder einen mitbenutzen kann....

Aber was anderes: Nach der Kanalumschaltung legt dein Quelltext ja 
direkt mit der Messung los und verwendet keine üblicherweise empfohlene 
Dummyreadouts. Sind deine Messungen trotzdem genau?

von Stefan E. (sternst)


Lesenswert?

Stefan B. schrieb:

> Aber was anderes: Nach der Kanalumschaltung legt dein Quelltext ja
> direkt mit der Messung los und verwendet keine üblicherweise empfohlene
> Dummyreadouts.

Das ist doch nur ein (anscheinend nicht tot zu kriegender) Mythos(*).
Dummyreads sind nötig nach dem Umschalten der Referenz, nicht dem Kanal 
(außer im Differential-Mode).

(*) Oder war das vielleicht mal bei den Uralt-AVRs nötig?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Danke! Es ist im Atmega128 Datenblatt so beschrieben, wie du schreibst! 
Dummyread bei Referenzwechsel oder Wechsel bei differential channels

von Owen S. (senmeis)


Lesenswert?

Vielen Dank. Die Voraussetzung für die Methode mit Timer ist jedoch, 
dass die Bearbeitungsdauer der A/D-Wandlung kürzer als 1ms sein muss. 
Ich denke, dies wird immer erfüllt.

Cu
Senmeis

von Matthias L. (Gast)


Lesenswert?

>Vielen Dank. Die Voraussetzung für die Methode mit Timer ist jedoch,
>dass die Bearbeitungsdauer der A/D-Wandlung kürzer als 1ms sein muss.
>Ich denke, dies wird immer erfüllt.
1
void 1ms_TimerInterrupt(void)
2
3
ISR(ADC_vect)

Ich würde den ADC-Int weglassen und das ADC-Ergebnis einfach im nächsten 
1ms-Int abholen, vor dem Start der nächsten Wandlung.

von Werner B. (werner-b)


Lesenswert?

Matthias Lipinsky schrieb:
>>Vielen Dank. Die Voraussetzung für die Methode mit Timer ist jedoch,
>>dass die Bearbeitungsdauer der A/D-Wandlung kürzer als 1ms sein muss.
>>Ich denke, dies wird immer erfüllt.
>
>
1
> void 1ms_TimerInterrupt(void)
2
> 
3
> ISR(ADC_vect)
4
>
>
> Ich würde den ADC-Int weglassen und das ADC-Ergebnis einfach im nächsten
> 1ms-Int abholen, vor dem Start der nächsten Wandlung.

Im Gegenteil. Den ADC durch den Timer triggern lassen und optional, 
falls der Timer Interrupt nicht verwendet wird (den benötigt man 
eigentlich nicht) in der darauffolgenden ADC ISR das Interrupt-Flag des 
Timer zurücksetzen. Das ergibt die genauesten Abtastintervalle die auf 
einem AVR möglich sind, da nicht einmal mehr die zufällige Interrupt 
Latency Time des Timer Interrupts eine Rolle spielt.

Ausschnitt einer ADC Initilisierung (für einen ATMega32)
1
...
2
  // ADC Trigger Source = Timer0 Compare Match
3
  SFIOR |= (1 << ADTS1) | (1 << ADTS0);
4
5
  ADCSRA = ADC_DIVISOR |      // Pick the fastest sample frequency (smallest divisor) we can use 
6
     _BV(ADEN)|      // ADC an
7
     _BV(ADSC)|      // Beginne mit der Konvertierung
8
     _BV(ADIE)|      // ADC-Interrupt an
9
     _BV(ADATE);     // Auto trigger on
10
  TIFR =  _BV(OCF0); // Reset OCF0 - Flag
11
...

Und der zugehörigen ADC-ISR
1
...
2
ISR(ADC_vect)
3
{
4
  int16_t adc = ADCW;      // ADC einlesen
5
6
  TIFR =  _BV(OCF0);      // Reset OCF0 - Flag
7
...
8
}
9
...

von SF (Gast)


Lesenswert?

So ähnlich würde ich es auch machen, nur leider hat der Mega128 keine 
Auto Trigger Funktionalität. Der ist einfach zu alt dafür...

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.