A/D-Routine im Atmega128

OP #1606392
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
#1606478
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?
Gast #1607608
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.
#1608785
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
...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren